endif(NOT CMAKE_BUILD_TYPE)
enable_language(C)
+enable_language(CXX)
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_VENDOR "gromacs.org")
list(APPEND GMX_EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
endif (GMX_DLOPEN)
+find_package(GTest)
+find_package(GMock)
+
########################################################################
# Generate development version info for cache
########################################################################
########################################################################
add_definitions( -DHAVE_CONFIG_H )
+include_directories(${CMAKE_SOURCE_DIR}/src)
+# Required for config.h, maybe should only be set in src/CMakeLists.txt
include_directories(${CMAKE_BINARY_DIR}/src)
-include_directories(${CMAKE_SOURCE_DIR}/include)
+# Required for now to make old code compile
+include_directories(${CMAKE_SOURCE_DIR}/src/gromacs/legacyheaders)
include(gmxCheckBuildUserTime)
gmx_check_build_user_time(BUILD_TIME BUILD_USER BUILD_MACHINE)
endif()
add_subdirectory(share)
-add_subdirectory(include)
add_subdirectory(man)
add_subdirectory(src)
add_subdirectory(scripts)
mark_as_advanced(BUILD_TESTING)
IF(BUILD_TESTING)
enable_testing()
- add_test(TestBuildAll make)
add_subdirectory(tests)
ENDIF()
+++ /dev/null
-Makefile
-Makefile.in
--- /dev/null
+# Locate the Google C++ Mocking Framework.
+#
+# Defines the following variables:
+#
+# GMOCK_FOUND - Found the Google Mocking framework
+# GMOCK_INCLUDE_DIRS - Include directories
+#
+# Also defines the library variables below as normal
+# variables. These contain debug/optimized keywords when
+# a debugging library is found.
+#
+# GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock-main
+# GMOCK_LIBRARIES - libgmock
+# GMOCK_MAIN_LIBRARIES - libgmock-main
+#
+# Accepts the following variables as input:
+#
+# GMOCK_ROOT - (as a CMake or environment variable)
+# The root directory of the gmock install prefix
+#
+# GMOCK_MSVC_SEARCH - If compiling with MSVC, this variable can be set to
+# "MD" or "MT" to enable searching a GTest build tree
+# (defaults: "MD")
+#
+#-----------------------
+# Example Usage:
+#
+# enable_testing()
+# find_package(GMock REQUIRED)
+# include_directories(${GMOCK_INCLUDE_DIRS})
+#
+# add_executable(foo foo.cc)
+# target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES})
+#
+# add_test(AllTestsInFoo foo)
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2009 Philip Lowman <philip@yhbt.com>
+# Copyright 2009 Daniel Blezek <blezek@gmail.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+#
+# Adapted from FindGTest.cmake in the CMake distribution by
+# Teemu Murtola <teemu.murtola@cbr.su.se>.
+
+function(_gmock_append_debugs _endvar _library)
+ if(${_library} AND ${_library}_DEBUG)
+ set(_output optimized ${${_library}} debug ${${_library}_DEBUG})
+ else()
+ set(_output ${${_library}})
+ endif()
+ set(${_endvar} ${_output} PARENT_SCOPE)
+endfunction()
+
+function(_gmock_find_library _name)
+ find_library(${_name}
+ NAMES ${ARGN}
+ HINTS
+ $ENV{GMOCK_ROOT}
+ ${GMOCK_ROOT}
+ PATH_SUFFIXES ${_gmock_libpath_suffixes}
+ )
+ mark_as_advanced(${_name})
+endfunction()
+
+#
+
+find_package(GTest)
+
+if(NOT DEFINED GMOCK_MSVC_SEARCH)
+ set(GMOCK_MSVC_SEARCH MD)
+endif()
+
+set(_gmock_libpath_suffixes lib)
+if(MSVC)
+ if(GMOCK_MSVC_SEARCH STREQUAL "MD")
+ list(APPEND _gmock_libpath_suffixes
+ msvc/gmock-md/Debug
+ msvc/gmock-md/Release)
+ elseif(GMOCK_MSVC_SEARCH STREQUAL "MT")
+ list(APPEND _gmock_libpath_suffixes
+ msvc/gmock/Debug
+ msvc/gmock/Release)
+ endif()
+endif()
+
+
+find_path(GMOCK_INCLUDE_DIR gmock/gmock.h
+ HINTS
+ $ENV{GMOCK_ROOT}/include
+ ${GMOCK_ROOT}/include
+)
+mark_as_advanced(GMOCK_INCLUDE_DIR)
+
+if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD")
+ # The provided /MD project files for Google Mock add -md suffixes to the
+ # library names.
+ _gmock_find_library(GMOCK_LIBRARY gmock-md gmock)
+ _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd)
+ _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main)
+ _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind)
+else()
+ _gmock_find_library(GMOCK_LIBRARY gmock)
+ _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd)
+ _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main)
+ _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind)
+endif()
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY)
+
+if(GMOCK_FOUND)
+ set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIRS})
+ _gmock_append_debugs(GMOCK_LIBRARIES GMOCK_LIBRARY)
+ set(GMOCK_LIBRARIES ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES})
+ _gmock_append_debugs(GMOCK_MAIN_LIBRARIES GMOCK_MAIN_LIBRARY)
+ set(GMOCK_MAIN_LIBRARIES ${GMOCK_MAIN_LIBRARIES} ${GTEST_LIBRARIES})
+ set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES})
+endif()
+
--- /dev/null
+# Locate the Google C++ Testing Framework.
+#
+# Defines the following variables:
+#
+# GTEST_FOUND - Found the Google Testing framework
+# GTEST_INCLUDE_DIRS - Include directories
+#
+# Also defines the library variables below as normal
+# variables. These contain debug/optimized keywords when
+# a debugging library is found.
+#
+# GTEST_BOTH_LIBRARIES - Both libgtest & libgtest-main
+# GTEST_LIBRARIES - libgtest
+# GTEST_MAIN_LIBRARIES - libgtest-main
+#
+# Accepts the following variables as input:
+#
+# GTEST_ROOT - (as a CMake or environment variable)
+# The root directory of the gtest install prefix
+#
+# GTEST_MSVC_SEARCH - If compiling with MSVC, this variable can be set to
+# "MD" or "MT" to enable searching a GTest build tree
+# (defaults: "MD")
+#
+#-----------------------
+# Example Usage:
+#
+# enable_testing()
+# find_package(GTest REQUIRED)
+# include_directories(${GTEST_INCLUDE_DIRS})
+#
+# add_executable(foo foo.cc)
+# target_link_libraries(foo ${GTEST_BOTH_LIBRARIES})
+#
+# add_test(AllTestsInFoo foo)
+#
+#-----------------------
+#
+# If you would like each Google test to show up in CTest as
+# a test you may use the following macro.
+# NOTE: It will slow down your tests by running an executable
+# for each test and test fixture. You will also have to rerun
+# CMake after adding or removing tests or test fixtures.
+#
+# GTEST_ADD_TESTS(executable extra_args ARGN)
+# executable = The path to the test executable
+# extra_args = Pass a list of extra arguments to be passed to
+# executable enclosed in quotes (or "" for none)
+# ARGN = A list of source files to search for tests & test
+# fixtures.
+#
+# Example:
+# set(FooTestArgs --foo 1 --bar 2)
+# add_executable(FooTest FooUnitTest.cc)
+# GTEST_ADD_TESTS(FooTest "${FooTestArgs}" FooUnitTest.cc)
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2009 Philip Lowman <philip@yhbt.com>
+# Copyright 2009 Daniel Blezek <blezek@gmail.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distributed this file outside of CMake, substitute the full
+# License text for the above reference.)
+#
+# Thanks to Daniel Blezek <blezek@gmail.com> for the GTEST_ADD_TESTS code
+
+function(GTEST_ADD_TESTS executable extra_args)
+ if(NOT ARGN)
+ message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS")
+ endif()
+ foreach(source ${ARGN})
+ file(READ "${source}" contents)
+ string(REGEX MATCHALL "TEST_?F?\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents})
+ foreach(hit ${found_tests})
+ string(REGEX REPLACE ".*\\( *([A-Za-z_0-9]+), *([A-Za-z_0-9]+) *\\).*" "\\1.\\2" test_name ${hit})
+ add_test(${test_name} ${executable} --gtest_filter=${test_name} ${extra_args})
+ endforeach()
+ endforeach()
+endfunction()
+
+function(_gtest_append_debugs _endvar _library)
+ if(${_library} AND ${_library}_DEBUG)
+ set(_output optimized ${${_library}} debug ${${_library}_DEBUG})
+ else()
+ set(_output ${${_library}})
+ endif()
+ set(${_endvar} ${_output} PARENT_SCOPE)
+endfunction()
+
+function(_gtest_find_library _name)
+ find_library(${_name}
+ NAMES ${ARGN}
+ HINTS
+ $ENV{GTEST_ROOT}
+ ${GTEST_ROOT}
+ PATH_SUFFIXES ${_gtest_libpath_suffixes}
+ )
+ mark_as_advanced(${_name})
+endfunction()
+
+#
+
+if(NOT DEFINED GTEST_MSVC_SEARCH)
+ set(GTEST_MSVC_SEARCH MD)
+endif()
+
+set(_gtest_libpath_suffixes lib)
+if(MSVC)
+ if(GTEST_MSVC_SEARCH STREQUAL "MD")
+ list(APPEND _gtest_libpath_suffixes
+ msvc/gtest-md/Debug
+ msvc/gtest-md/Release)
+ elseif(GTEST_MSVC_SEARCH STREQUAL "MT")
+ list(APPEND _gtest_libpath_suffixes
+ msvc/gtest/Debug
+ msvc/gtest/Release)
+ endif()
+endif()
+
+
+find_path(GTEST_INCLUDE_DIR gtest/gtest.h
+ HINTS
+ $ENV{GTEST_ROOT}/include
+ ${GTEST_ROOT}/include
+)
+mark_as_advanced(GTEST_INCLUDE_DIR)
+
+if(MSVC AND GTEST_MSVC_SEARCH STREQUAL "MD")
+ # The provided /MD project files for Google Test add -md suffixes to the
+ # library names.
+ _gtest_find_library(GTEST_LIBRARY gtest-md gtest)
+ _gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd)
+ _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main)
+ _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind)
+else()
+ _gtest_find_library(GTEST_LIBRARY gtest)
+ _gtest_find_library(GTEST_LIBRARY_DEBUG gtestd)
+ _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main)
+ _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind)
+endif()
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY)
+
+if(GTEST_FOUND)
+ set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR})
+ _gtest_append_debugs(GTEST_LIBRARIES GTEST_LIBRARY)
+ _gtest_append_debugs(GTEST_MAIN_LIBRARIES GTEST_MAIN_LIBRARY)
+ set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
+endif()
+
if (NOT DEFINED TMPI_ATOMICS)
try_compile(TEST_ATOMICS "${CMAKE_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestAtomics.c"
- COMPILE_DEFINITIONS "-I${CMAKE_SOURCE_DIR}/include" )
+ COMPILE_DEFINITIONS "-I${CMAKE_SOURCE_DIR}/src/gromacs/legacyheaders" )
if (TEST_ATOMICS)
message(STATUS "Atomics found")
+++ /dev/null
-Makefile.in
-Makefile
-config.h
-stamp-h.in
+++ /dev/null
-# includes: Nothing to build, just installation
-install(DIRECTORY . DESTINATION ${INCL_INSTALL_DIR}/gromacs
- COMPONENT development
- PATTERN "Makefile*" EXCLUDE
- PATTERN "CMake*" EXCLUDE
- PATTERN "cmake*" EXCLUDE
- PATTERN "*~" EXCLUDE
-)
+++ /dev/null
-## Process this file with automake to produce Makefile.in
-#
-# Don't edit - this file is generated automatically from Makefile.am
-#
-
-SUBDIRS = . types thread_mpi
-
-
-pkginclude_HEADERS = \
-3dview.h \
-assert.h \
-atomprop.h \
-bondf.h \
-calcgrid.h \
-calch.h \
-calcmu.h \
-centerofmass.h \
-chargegroup.h \
-checkpoint.h \
-confio.h \
-constr.h \
-copyrite.h \
-coulomb.h \
-dihre.h \
-displacement.h \
-disre.h \
-do_fit.h \
-domdec.h \
-domdec_network.h \
-ebin.h \
-edsam.h \
-enxio.h \
-ffscanf.h \
-filenm.h \
-force.h \
-futil.h \
-gbutil.h \
-gen_ad.h \
-genborn.h \
-gmx_ana.h \
-gmx_arpack.h \
-gmx_blas.h \
-gmx_cyclecounter.h \
-gmx_fatal.h \
-gmx_fft.h \
-gmx_ga2la.h \
-gmx_lapack.h \
-gmx_matrix.h \
-gmx_parallel_3dfft.h \
-gmx_random.h \
-gmx_sort.h \
-gmx_sse2_single.h \
-gmx_statistics.h \
-gmx_system_xdr.h \
-gmx_wallcycle.h \
-gmxcomplex.h \
-gmxcpp.h \
-gmxfio.h \
-gpp_atomtype.h \
-gpp_nextnb.h \
-grompp.h \
-gstat.h \
-hackblock.h \
-histogram.h \
-index.h \
-indexutil.h \
-inputrec.h \
-invblock.h \
-macros.h \
-magic.h \
-main.h \
-maths.h \
-matio.h \
-md5.h \
-mdatoms.h \
-mdebin.h \
-mdrun.h \
-mpelogging.h \
-mshift.h \
-mtop_util.h\
-mtxio.h \
-mvdata.h \
-names.h \
-nbsearch.h \
-network.h \
-nonbonded.h \
-nrama.h \
-nrjac.h \
-nrnb.h \
-ns.h \
-nsgrid.h \
-orires.h \
-partdec.h \
-pbc.h \
-pdbio.h \
-pdb2top.h \
-perf_est.h \
-physics.h \
-pme.h \
-poscalc.h \
-position.h \
-pppm.h \
-princ.h \
-pull.h \
-qmmm.h \
-random.h \
-rbin.h \
-rdgroup.h \
-readinp.h \
-resall.h \
-rmpbc.h \
-selection.h \
-selmethod.h \
-selparam.h \
-selvalue.h \
-sfactor.h \
-shellfc.h \
-shift.h \
-smalloc.h \
-sortwater.h \
-sparsematrix.h \
-split.h \
-splitter.h \
-statutil.h \
-strdb.h \
-string2.h \
-symtab.h \
-sysstuff.h \
-tgroup.h \
-topsort.h \
-toputil.h \
-tpxio.h \
-trajana.h \
-trnio.h \
-txtdump.h \
-typedefs.h \
-update.h \
-vcm.h \
-vec.h \
-viewit.h \
-vmdio.h \
-vsite.h \
-warninp.h \
-wgms.h \
-wman.h \
-writeps.h \
-xdrf.h \
-xtcio.h \
-xvgr.h \
-thread_mpi.h \
-tmpi.h \
-mainpage.h \
-molfile_plugin.h \
-vmddlopen.h \
-vmdplugin.h \
-oenv.h \
-sighandler.h \
-gmx_sse2_double.h
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for calculation of centers of mass/geometry.
- *
- * This header defines a few functions that can be used to calculate
- * centers of mass/geometry for a group of atoms.
- * These routines can be used independently of the other parts of the
- * library, but they are also used internally by the selection engine.
- * In most cases, it should not be necessary to call these functions
- * directly.
- * Instead, one should write an analysis tool such that it gets all
- * positions through selections.
- *
- * The functions in the header can be divided into a few groups based on the
- * parameters they take. The simplest group of functions calculates the center
- * of a single group of atoms:
- * - gmx_calc_cog(): Calculates the center of geometry (COG) of a given
- * group of atoms.
- * - gmx_calc_com(): Calculates the center of mass (COM) of a given group
- * of atoms.
- * - gmx_calc_comg(): Calculates either the COM or COG, based on a
- * gmx_boolean flag.
- *
- * A second set of routines is provided for calculating the centers for groups
- * that wrap over periodic boundaries (gmx_calc_cog_pbc(), gmx_calc_com_pbc(),
- * gmx_calc_comg_pbc()). These functions are slower, because they need to
- * adjust the center iteratively.
- *
- * It is also possible to calculate centers for several groups of atoms in
- * one call. The functions gmx_calc_cog_block(), gmx_calc_com_block() and
- * gmx_calc_comg_block() take an index group and a partitioning of that index
- * group (as a \c t_block structure), and calculate the centers for
- * each group defined by the \c t_block structure separately.
- *
- * Finally, there is a function gmx_calc_comg_blocka() that takes both the
- * index group and the partitioning as a single \c t_blocka structure.
- */
-#ifndef CENTEROFMASS_H
-#define CENTEROFMASS_H
-
-#include "typedefs.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/** Calculate a single center of geometry. */
-int
-gmx_calc_cog(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout);
-/** Calculate a single center of mass. */
-int
-gmx_calc_com(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout);
-/** Calculate force on a single center of geometry. */
-int
-gmx_calc_cog_f(t_topology *top, rvec f[], int nrefat, atom_id index[], rvec fout);
-/** Calculate a single center of mass/geometry. */
-int
-gmx_calc_comg(t_topology *top, rvec x[], int nrefat, atom_id index[],
- gmx_bool bMass, rvec xout);
-/** Calculate force on a single center of mass/geometry. */
-int
-gmx_calc_comg_f(t_topology *top, rvec f[], int nrefat, atom_id index[],
- gmx_bool bMass, rvec fout);
-
-/** Calculate a single center of geometry iteratively, taking PBC into account. */
-int
-gmx_calc_cog_pbc(t_topology *top, rvec x[], t_pbc *pbc,
- int nrefat, atom_id index[], rvec xout);
-/** Calculate a single center of mass iteratively, taking PBC into account. */
-int
-gmx_calc_com_pbc(t_topology *top, rvec x[], t_pbc *pbc,
- int nrefat, atom_id index[], rvec xout);
-/** Calculate a single center of mass/geometry iteratively with PBC. */
-int
-gmx_calc_comg_pbc(t_topology *top, rvec x[], t_pbc *pbc,
- int nrefat, atom_id index[], gmx_bool bMass, rvec xout);
-
-/** Calculate centers of geometry for a blocked index. */
-int
-gmx_calc_cog_block(t_topology *top, rvec x[], t_block *block,
- atom_id index[], rvec xout[]);
-/** Calculate centers of mass for a blocked index. */
-int
-gmx_calc_com_block(t_topology *top, rvec x[], t_block *block,
- atom_id index[], rvec xout[]);
-/** Calculate forces on centers of geometry for a blocked index. */
-int
-gmx_calc_cog_f_block(t_topology *top, rvec f[], t_block *block,
- atom_id index[], rvec fout[]);
-/** Calculate centers of mass/geometry for a blocked index. */
-int
-gmx_calc_comg_block(t_topology *top, rvec x[], t_block *block,
- atom_id index[], gmx_bool bMass, rvec xout[]);
-/** Calculate forces on centers of mass/geometry for a blocked index. */
-int
-gmx_calc_comg_f_block(t_topology *top, rvec f[], t_block *block,
- atom_id index[], gmx_bool bMass, rvec fout[]);
-/** Calculate centers of mass/geometry for a set of blocks; */
-int
-gmx_calc_comg_blocka(t_topology *top, rvec x[], t_blocka *block,
- gmx_bool bMass, rvec xout[]);
-/** Calculate forces on centers of mass/geometry for a set of blocks; */
-int
-gmx_calc_comg_f_blocka(t_topology *top, rvec x[], t_blocka *block,
- gmx_bool bMass, rvec xout[]);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2008, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
- */
-
-#ifndef _GMX_STATS_H
-#define _GMX_STATS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "typedefs.h"
-
-typedef struct gmx_stats *gmx_stats_t;
-
-/* Error codes returned by the routines */
-enum { estatsOK, estatsNO_POINTS, estatsNO_MEMORY, estatsERROR,
- estatsINVALID_INPUT, estatsNOT_IMPLEMENTED, estatsNR };
-
-enum { elsqWEIGHT_NONE, elsqWEIGHT_X, elsqWEIGHT_Y,
- elsqWEIGHT_XY, elsqWEIGHT_NR };
-
-enum { ehistoX, ehistoY, ehistoNR };
-
-gmx_stats_t gmx_stats_init();
-
-int gmx_stats_done(gmx_stats_t stats);
-
-/* Remove outliers from a straight line, where level in units of
- sigma. Level needs to be larger than one obviously. */
-int gmx_stats_remove_outliers(gmx_stats_t stats,double level);
-
-int gmx_stats_add_point(gmx_stats_t stats,double x,double y,
- double dx,double dy);
-
-/* The arrays dx and dy may be NULL if no uncertainties are available,
- in that case zero uncertainties will be assumed. */
-int gmx_stats_add_points(gmx_stats_t stats,int n,real *x,real *y,
- real *dx,real *dy);
-
-/* Return the data points one by one. Return estatsOK while there are
- more points, and returns estatsNOPOINTS when the last point has
- been returned. Should be used in a while loop. Variables for either
- pointer may be NULL, in which case the routine can be used as an
- expensive point counter. */
-int gmx_stats_get_point(gmx_stats_t stats,real *x,real *y,
- real *dx,real *dy);
-
-/* Fit the data to y = ax + b, possibly weighted, if uncertainties
- have been input. Returns slope in *a and intercept in b, *return
- sigmas in *da and *db respectively. Returns normalized *quality of
- fit in *chi2 and correlation of fit with data in Rfit. chi2, Rfit,
- da and db may be NULL. */
-int gmx_stats_get_ab(gmx_stats_t stats,int weight,
- real *a,real *b,
- real *da,real *db,real *chi2,real *Rfit);
-
-/* Fit the data to y = ax, possibly weighted, if uncertainties have
- been input. Returns slope in *a, sigma in a in *da, and normalized
- quality of fit in *chi2 and correlation of fit with data in
- Rfit. chi2, Rfit and da may be NULL. */
-int gmx_stats_get_a(gmx_stats_t stats,int weight,
- real *a,real *da,real *chi2,real *Rfit);
-
-/* Return the correlation coefficient between the data (x and y) as
- input to the structure. */
-int gmx_stats_get_corr_coeff(gmx_stats_t stats,real *R);
-
-/* Returns the root mean square deviation between x and y values. */
-int gmx_stats_get_rmsd(gmx_stats_t gstats,real *rmsd);
-
-int gmx_stats_get_npoints(gmx_stats_t stats,int *N);
-
-int gmx_stats_get_average(gmx_stats_t stats,real *aver);
-
-int gmx_stats_get_sigma(gmx_stats_t stats,real *sigma);
-
-int gmx_stats_get_error(gmx_stats_t stats,real *error);
-
-/* Get all three of the above. Pointers may be null, in which case no
- assignment will be done. */
-int gmx_stats_get_ase(gmx_stats_t gstats,real *aver,real *sigma,real *error);
-
-/* Dump the x, y, dx, dy data to a text file */
-int gmx_stats_dump_xy(gmx_stats_t gstats,FILE *fp);
-
-/* Make a histogram of the data present. Uses either bindwith to
- determine the number of bins, or nbins to determine the binwidth,
- therefore one of these should be zero, but not the other. If *nbins = 0
- the number of bins will be returned in this variable. ehisto should be one of
- ehistoX or ehistoY. If
- normalized not equal to zero, the integral of the histogram will be
- normalized to one. The output is in two arrays, *x and *y, to which
- you should pass a pointer. Memory for the arrays will be allocated
- as needed. Function returns one of the estats codes. */
-int gmx_stats_make_histogram(gmx_stats_t gstats,real binwidth,int *nbins,
- int ehisto,
- int normalized,real **x,real **y);
-
-/* Return message belonging to error code */
-const char *gmx_stats_message(int estats);
-
-/****************************************************
- * Some statistics utilities for convenience: useful when a complete data
- * set is available already from another source, e.g. an xvg file.
- ****************************************************/
-int lsq_y_ax(int n, real x[], real y[], real *a);
-/* Fit a straight line y=ax thru the n data points x, y, return the
- slope in *a. Return value can be estatsOK, or something else. */
-
-int lsq_y_ax_b(int n, real x[], real y[], real *a, real *b,real *r,
- real *chi2);
-/* Fit a straight line y=ax+b thru the n data points x,y.
- * Returns the "fit quality" sigma = sqrt(chi^2/(n-2)).
- * The correlation coefficient is returned in r.
- */
-
-int lsq_y_ax_b_xdouble(int n, double x[], real y[],
- real *a, real *b,real *r,real *chi2);
-/* As lsq_y_ax_b, but with x in double precision.
- */
-
-int lsq_y_ax_b_error(int n, real x[], real y[], real dy[],
- real *a, real *b, real *da, real *db,
- real *r,real *chi2);
-/* Fit a straight line y=ax+b thru the n data points x,y, with sigma dy
- * Returns the "fit quality" sigma = sqrt(chi^2/(n-2)).
- * The correlation coefficient is returned in r.
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2008, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
- */
-
-
-#ifndef _gmx_wallcycle_h
-#define _gmx_wallcycle_h
-
-#include <stdio.h>
-#include "typedefs.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
- enum { ewcRUN, ewcSTEP, ewcPPDURINGPME, ewcDOMDEC, ewcDDCOMMLOAD, ewcDDCOMMBOUND, ewcVSITECONSTR, ewcPP_PMESENDX, ewcMOVEX, ewcNS, ewcGB, ewcFORCE, ewcMOVEF, ewcPMEMESH, ewcPME_REDISTXF, ewcPME_SPREADGATHER, ewcPME_FFT, ewcPME_SOLVE, ewcPMEWAITCOMM, ewcPP_PMEWAITRECVF, ewcVSITESPREAD, ewcTRAJ, ewcUPDATE, ewcCONSTR, ewcMoveE, ewcTEST, ewcNR };
-
-gmx_bool wallcycle_have_counter(void);
-/* Returns if cycle counting is supported */
-
-gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr);
-/* Returns the wall cycle structure.
- * Returns NULL when cycle counting is not supported.
- */
-
-void wallcycle_start(gmx_wallcycle_t wc, int ewc);
-/* Set the start cycle count for ewc */
-
-double wallcycle_stop(gmx_wallcycle_t wc, int ewc);
-/* Stop the cycle count for ewc, returns the last cycle count */
-
-void wallcycle_reset_all(gmx_wallcycle_t wc);
-/* Resets all cycle counters to zero */
-
-void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc,double cycles[]);
-/* Sum the cycles over the nodes in cr->mpi_comm_mysim */
-
-void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
- gmx_wallcycle_t wc, double cycles[]);
-/* Print the cycle and time accounting */
-
-gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc);
-/* Return reset_counters from wc struct */
-
-void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters);
-/* Set reset_counters */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _gmx_wallcycle_h */
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gromacs Runs On Most of All Computer Systems
- */
-
-#ifndef _gmxfio_h
-#define _gmxfio_h
-
-#include <stdio.h>
-#include "sysstuff.h"
-#include "typedefs.h"
-#include "xdrf.h"
-#include "futil.h"
-
-/* types */
-
-
-/* Enumerated for different items in files */
-enum { eitemHEADER, eitemIR, eitemBOX,
- eitemTOP, eitemX, eitemV, eitemF, eitemNR };
-
-/* Enumerated for data types in files */
-enum { eioREAL, eioFLOAT, eioDOUBLE, eioINT, eioGMX_LARGE_INT,
- eioUCHAR, eioNUCHAR, eioUSHORT,
- eioRVEC, eioNRVEC, eioIVEC, eioSTRING, eioNR };
-
-typedef struct t_fileio t_fileio;
-
-extern const char *itemstr[eitemNR];
-extern const char *comment_str[eitemNR];
-
-/* NOTE ABOUT THREAD SAFETY:
-
- The functions are all thread-safe, provided that two threads don't
- do something silly like closing the same file, or one thread
- accesses a file that has been closed by another.
- */
-
-/********************************************************
- * Open and Close
- ********************************************************/
-
-t_fileio *gmx_fio_open(const char *fn,const char *mode);
-/* Open a new file for reading or writing.
- * The file type will be deduced from the file name.
- * If fn is NULL, stdin / stdout will be used for Ascii I/O (TPA type)
- * mode may be "r", "w", or "a". You should append a "b" to the mode
- * if you are writing a binary file, but the routine will also
- * doublecheck it and try to do it if you forgot. This has no effect on
- * unix, but is important on windows.
- */
-
-int gmx_fio_close(t_fileio *fp);
-/* Close the file corresponding to fp (if not stdio)
- * The routine will exit when an invalid fio is handled.
- * Returns 0 on success.
- */
-
-int gmx_fio_fp_close(t_fileio *fp);
-/* Close the file corresponding to fp without closing the FIO entry
- * Needed e.g. for trxio because the FIO entries are used to store
- * additional data.
- * NOTE that the fp still needs to be properly closed with gmx_fio_close().
- * The routine will exit when an invalid fio is handled.
- * Returns 0 on success.
- */
-
-
-/* Open a file, return a stream, record the entry in internal FIO object */
-FILE* gmx_fio_fopen(const char *fn,const char *mode);
-
-/* Close a file previously opened with gmx_fio_fopen.
- * Do not mix these calls with standard fopen/fclose ones!
- * Returns 0 on success. */
-int gmx_fio_fclose(FILE *fp);
-
-
-
-/********************************************************
- * Change properties of the open file
- ********************************************************/
-
-void gmx_fio_setprecision(t_fileio *fio,gmx_bool bDouble);
-/* Select the floating point precision for reading and writing files */
-
-char *gmx_fio_getname(t_fileio *fio);
-/* Return the filename corresponding to the fio index */
-
-int gmx_fio_getftp(t_fileio *fio);
-/* Return the filetype corresponding to the fio index.
- There is as of now no corresponding setftp function because the file
- was opened as a specific file type and changing that midway is most
- likely an evil hack. */
-
-void gmx_fio_setdebug(t_fileio *fio,gmx_bool bDebug);
-/* Set the debug mode */
-
-gmx_bool gmx_fio_getdebug(t_fileio *fio);
-/* Return whether debug mode is on in fio */
-
-gmx_bool gmx_fio_getread(t_fileio *fio);
-/* Return whether read mode is on in fio */
-
-
-void gmx_fio_checktype(t_fileio *fio);
-/* Check whether the fio is of a sane type */
-
-/***************************************************
- * FILE Operations
- ***************************************************/
-
-void gmx_fio_rewind(t_fileio *fio);
-/* Rewind the tpa file in fio */
-
-int gmx_fio_flush(t_fileio *fio);
-/* Flush the fio, returns 0 on success */
-
-int gmx_fio_fsync(t_fileio *fio);
-/* fsync the fio, returns 0 on success.
- NOTE: don't use fsync function unless you're absolutely sure you need it
- because it deliberately interferes with the OS's caching mechanisms and
- can cause dramatically slowed down IO performance. Some OSes (Linux,
- for example), may implement fsync as a full sync() point. */
-
-gmx_off_t gmx_fio_ftell(t_fileio *fio);
-/* Return file position if possible */
-
-int gmx_fio_seek(t_fileio *fio,gmx_off_t fpos);
-/* Set file position if possible, quit otherwise */
-
-FILE *gmx_fio_getfp(t_fileio *fio);
-/* Return the file pointer itself */
-
-XDR *gmx_fio_getxdr(t_fileio *fio);
-/* Return the file pointer itself */
-
-
-
-
-
-/* Element with information about position in a currently open file.
- * gmx_off_t should be defined by autoconf if your system does not have it.
- * If you do not have it on some other platform you do not have largefile
- * support at all, and you can define it to int (or better, find out how to
- * enable large files). */
-typedef struct
-{
- char filename[STRLEN];
- gmx_off_t offset;
- unsigned char chksum[16];
- int chksum_size;
-}
-gmx_file_position_t;
-
-
-int gmx_fio_check_file_position(t_fileio *fio);
-/* Check if the file position is out of the range of off_t.
- * The result is stored along with the other file data of fio.
- */
-
-int gmx_fio_get_output_file_positions(gmx_file_position_t ** outputfiles,
- int *nfiles );
-/* Return the name and file pointer positions for all currently open
- * output files. This is used for saving in the checkpoint files, so we
- * can truncate output files upon restart-with-appending.
- *
- * For the first argument you should use a pointer, which will be set to
- * point to a list of open files.
- */
-
-t_fileio *gmx_fio_all_output_fsync(void);
-/* fsync all open output files. This is used for checkpointing, where
- we need to ensure that all output is actually written out to
- disk.
- This is most important in the case of some networked file systems,
- where data is not synced with the file server until close() or
- fsync(), so data could remain in cache for days.
- Note the caveats reported with gmx_fio_fsync().
-
- returns: NULL if no error occurred, or a pointer to the first file that
- failed if an error occurred
-*/
-
-
-int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
- unsigned char digest[]);
-
-
-int xtc_seek_frame(t_fileio *fio, int frame, int natoms);
-
-int xtc_seek_time(t_fileio *fio, real time, int natoms);
-
-
-/* Add this to the comment string for debugging */
-void gmx_fio_set_comment(t_fileio *fio, const char *comment);
-
-/* Remove previously set comment */
-void gmx_fio_unset_comment(t_fileio *fio);
-
-
-
-
-/********************************************************
- * Read and write
- ********************************************************/
-
-
-/* basic reading & writing */
-gmx_bool gmx_fio_reade_real(t_fileio *fio, real *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_float(t_fileio *fio, float *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_double(t_fileio *fio, double *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_int(t_fileio *fio, int *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_gmx_large_int(t_fileio *fio, gmx_large_int_t *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_uchar(t_fileio *fio, unsigned char *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_ushort(t_fileio *fio, unsigned short *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_rvec(t_fileio *fio, rvec *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_ivec(t_fileio *fio, ivec *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_reade_string(t_fileio *fio, char *item,
- const char *desc, const char *srcfile, int line);
-
-gmx_bool gmx_fio_writee_real(t_fileio *fio, real item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_float(t_fileio *fio, float item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_double(t_fileio *fio, double item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_int(t_fileio *fio, int item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_gmx_large_int(t_fileio *fio, gmx_large_int_t item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_uchar(t_fileio *fio, unsigned char item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_ushort(t_fileio *fio, unsigned short item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_rvec(t_fileio *fio, rvec *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_ivec(t_fileio *fio, ivec *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_writee_string(t_fileio *fio, const char *item,
- const char *desc, const char *srcfile, int line);
-
-/* reading or writing, depending on the file's opening mode string */
-gmx_bool gmx_fio_doe_real(t_fileio *fio, real *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_float(t_fileio *fio, float *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_double(t_fileio *fio, double *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_gmx_bool(t_fileio *fio, gmx_bool *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_int(t_fileio *fio, int *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_gmx_large_int(t_fileio *fio, gmx_large_int_t *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_uchar(t_fileio *fio, unsigned char *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_ushort(t_fileio *fio, unsigned short *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_rvec(t_fileio *fio, rvec *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_ivec(t_fileio *fio, ivec *item,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_doe_string(t_fileio *fio, char *item,
- const char *desc, const char *srcfile, int line);
-
-
-
-
-/* array reading & writing */
-gmx_bool gmx_fio_nreade_real(t_fileio *fio, real *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_float(t_fileio *fio, float *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_double(t_fileio *fio, double *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_int(t_fileio *fio, int *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, int n,
- const char *desc, const char *srcfile,
- int line);
-gmx_bool gmx_fio_nreade_uchar(t_fileio *fio, unsigned char *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_ushort(t_fileio *fio, unsigned short *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_rvec(t_fileio *fio, rvec *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_ivec(t_fileio *fio, ivec *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nreade_string(t_fileio *fio, char *item[], int n,
- const char *desc, const char *srcfile, int line);
-
-gmx_bool gmx_fio_nwritee_real(t_fileio *fio, const real *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_float(t_fileio *fio, const float *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_double(t_fileio *fio, const double *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_int(t_fileio *fio, const int *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_gmx_large_int(t_fileio *fio,
- const gmx_large_int_t *item, int n,
- const char *desc, const char *srcfile,
- int line);
-gmx_bool gmx_fio_nwritee_uchar(t_fileio *fio, const unsigned char *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_ushort(t_fileio *fio, const unsigned short *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_rvec(t_fileio *fio, const rvec *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_ivec(t_fileio *fio, const ivec *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_nwritee_string(t_fileio *fio, const char *item[], int n,
- const char *desc, const char *srcfile, int line);
-
-gmx_bool gmx_fio_ndoe_real(t_fileio *fio, real *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_float(t_fileio *fio, float *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_double(t_fileio *fio, double *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_gmx_bool(t_fileio *fio, gmx_bool *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_int(t_fileio *fio, int *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, int n,
- const char *desc, const char *srcfile,
- int line);
-gmx_bool gmx_fio_ndoe_uchar(t_fileio *fio, unsigned char *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_ushort(t_fileio *fio, unsigned short *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_rvec(t_fileio *fio, rvec *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_ivec(t_fileio *fio, ivec *item, int n,
- const char *desc, const char *srcfile, int line);
-gmx_bool gmx_fio_ndoe_string(t_fileio *fio, char *item[], int n,
- const char *desc, const char *srcfile, int line);
-
-
-
-/* convenience macros */
-#define gmx_fio_read_real(fio, item) gmx_fio_reade_real(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_float(fio, item) gmx_fio_reade_float(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_double(fio, item) gmx_fio_reade_double(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_int(fio, item) gmx_fio_reade_int(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_gmx_large_int(fio, item) gmx_fio_reade_gmx_large_int(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_uchar(fio, item) gmx_fio_reade_uchar(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_ushort(fio, item) gmx_fio_reade_ushort(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_rvec(fio, item) gmx_fio_reade_rvec(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_ivec(fio, item) gmx_fio_reade_ivec(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_read_string(fio, item) gmx_fio_reade_string(fio, item, (#item), __FILE__, __LINE__)
-
-#define gmx_fio_write_real(fio, item) gmx_fio_writee_real(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_float(fio, item) gmx_fio_writee_float(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_double(fio, item) gmx_fio_writee_double(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_int(fio, item) gmx_fio_writee_int(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_gmx_large_int(fio, item) gmx_fio_writee_gmx_large_int(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_uchar(fio, item) gmx_fio_writee_uchar(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_ushort(fio, item) gmx_fio_writee_ushort(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_rvec(fio, item) gmx_fio_writee_rvec(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_ivec(fio, item) gmx_fio_writee_ivec(fio, item, (#item), __FILE__, __LINE__)
-#define gmx_fio_write_string(fio, item) gmx_fio_writee_string(fio, item, (#item), __FILE__, __LINE__)
-
-#define gmx_fio_do_real(fio, item) gmx_fio_doe_real(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_float(fio, item) gmx_fio_doe_float(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_double(fio, item) gmx_fio_doe_double(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_gmx_bool(fio, item) gmx_fio_doe_gmx_bool(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_int(fio, item) gmx_fio_doe_int(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_gmx_large_int(fio, item) gmx_fio_doe_gmx_large_int(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_uchar(fio, item) gmx_fio_doe_uchar(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_ushort(fio, item) gmx_fio_doe_ushort(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_rvec(fio, item) gmx_fio_doe_rvec(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_ivec(fio, item) gmx_fio_doe_ivec(fio, &item, (#item), __FILE__, __LINE__)
-#define gmx_fio_do_string(fio, item) gmx_fio_doe_string(fio, item, (#item), __FILE__, __LINE__)
-
-
-
-
-#define gmx_fio_nread_real(fio, item, n) gmx_fio_nreade_real(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_float(fio, item, n) gmx_fio_nreade_float(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_double(fio, item, n) gmx_fio_nreade_double(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_int(fio, item, n) gmx_fio_nreade_int(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_gmx_large_int(fio, item, n) gmx_fio_nreade_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_uchar(fio, item, n) gmx_fio_nreade_uchar(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_ushort(fio, item, n) gmx_fio_nreade_ushort(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_rvec(fio, item, n) gmx_fio_nreade_rvec(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_ivec(fio, item, n) gmx_fio_nreade_ivec(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nread_string(fio, item, n) gmx_fio_nreade_string(fio, item, n, (#item), __FILE__, __LINE__)
-
-#define gmx_fio_nwrite_real(fio, item, n) gmx_fio_nwritee_real(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_float(fio, item, n) gmx_fio_nwritee_float(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_double(fio, item, n) gmx_fio_nwritee_double(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_int(fio, item, n) gmx_fio_nwritee_int(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_gmx_large_int(fio, item, n) gmx_fio_nwritee_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_uchar(fio, item, n) gmx_fio_nwritee_uchar(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_ushort(fio, item, n) gmx_fio_nwritee_ushort(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_rvec(fio, item, n) gmx_fio_nwritee_rvec(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_ivec(fio, item, n) gmx_fio_nwritee_ivec(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_nwrite_string(fio, item, n) gmx_fio_nwritee_string(fio, item, n, (#item), __FILE__, __LINE__)
-
-#define gmx_fio_ndo_real(fio, item, n) gmx_fio_ndoe_real(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_float(fio, item, n) gmx_fio_ndoe_float(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_double(fio, item, n) gmx_fio_ndoe_double(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_gmx_bool(fio, item, n) gmx_fio_ndoe_gmx_bool(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_int(fio, item, n) gmx_fio_ndoe_int(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_gmx_large_int(fio, item, n) gmx_fio_ndoe_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_uchar(fio, item, n) gmx_fio_ndoe_uchar(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_ushort(fio, item, n) gmx_fio_ndoe_ushort(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_rvec(fio, item, n) gmx_fio_ndoe_rvec(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_ivec(fio, item, n) gmx_fio_ndoe_ivec(fio, item, n, (#item), __FILE__, __LINE__)
-#define gmx_fio_ndo_string(fio, item, n) gmx_fio_ndoe_string(fio, item, n, (#item), __FILE__, __LINE__)
-
-
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for handling index files and index groups.
- *
- * The API contains functions and data structures for handling index
- * files more conveniently than as several separate variables.
- * In addition to basic functions for initializing the data structures and
- * making copies, functions are provided for performing (most) set operations
- * on sorted index groups.
- * There is also a function for partitioning a index group based on
- * topology information such as residues or molecules.
- * Finally, there is a set of functions for constructing mappings between
- * an index group and its subgroups such.
- * These can be used with dynamic index group in calculations if one
- * needs to have a unique ID for each possible atom/residue/molecule in the
- * selection, e.g., for analysis of dynamics or for look-up tables.
- *
- * Mostly, these functions are used internally by the library and the
- * selection engine.
- * However, some of the checking functions can be useful in user code to
- * check the validity of input groups.
- * Also, the mapping functions are useful when dealing with dynamic index
- * groups.
- */
-#ifndef INDEXUTIL_H
-#define INDEXUTIL_H
-
-#include "typedefs.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/** Stores a set of index groups. */
-typedef struct gmx_ana_indexgrps_t gmx_ana_indexgrps_t;
-
-/*! \brief
- * Specifies the type of index partition or index mapping in several contexts.
- *
- * \see gmx_ana_index_make_block(), gmx_ana_indexmap_init()
- */
-typedef enum
-{
- INDEX_UNKNOWN, /**< Unknown index type.*/
- INDEX_ATOM, /**< Each atom in a separate block.*/
- INDEX_RES, /**< Each residue in a separate block.*/
- INDEX_MOL, /**< Each molecule in a separate block.*/
- INDEX_ALL /**< All atoms in a single block.*/
-} e_index_t;
-
-/*! \brief
- * Stores a single index group.
- */
-typedef struct gmx_ana_index_t
-{
- /** Number of atoms. */
- int isize;
- /** List of atoms. */
- atom_id *index;
- /** Group name. */
- char *name;
- /** Number of items allocated for \p index. */
- int nalloc_index;
-} gmx_ana_index_t;
-
-/*! \brief
- * Data structure for calculating index group mappings.
- */
-typedef struct gmx_ana_indexmap_t
-{
- /** Type of the mapping. */
- e_index_t type;
- /*! \brief
- * Current number of mapped values.
- *
- * This is the current number of values in the \p refid and \p mapid
- * arrays.
- * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), this
- * is always equal to \p b.nr, i.e., the number of blocks in the
- * original index group.
- */
- int nr;
- /*! \brief
- * Current reference IDs.
- *
- * This array provides a mapping from the current index group (last given
- * to gmx_ana_indexmap_update()) to the blocks in \p b, i.e., the
- * original index group used in gmx_ana_indexmap_init().
- * The mapping is zero-based.
- * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), the indices
- * for blocks not present in the current group are set to -1, otherwise
- * they are removed completely and the \p nr field updated.
- */
- int *refid;
- /*! \brief
- * Current mapped IDs.
- *
- * This array provides an arbitrary mapping from the current index group
- * to the original index group. Instead of a zero-based mapping, the
- * values from the \p orgid array are used. That is,
- * \c mapid[i]=orgid[refid[i]].
- * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), this array
- * equals \p orgid.
- */
- int *mapid;
- /*! \brief
- * Mapped block structure.
- *
- * A block structure that corresponds to the current index group.
- */
- t_block mapb;
-
- /*! \brief
- * Arbitrary ID numbers for the blocks.
- *
- * This array has \p b.nr elements, each defining an ID number for a
- * block in \p b.
- * These are initialized in gmx_ana_indexmap_init() based on the type:
- * - \ref INDEX_ATOM : the atom indices
- * - \ref INDEX_RES : the residue numbers
- * - \ref INDEX_MOL : the molecule numbers
- *
- * All the above numbers are zero-based.
- * After gmx_ana_indexmap_init(), the user is free to change these values
- * if the above are not appropriate.
- * The mapped values can be read through \p mapid.
- */
- int *orgid;
-
- /*! \brief
- * Block data that defines the mapping (internal use only).
- *
- * The data is initialized by gmx_ana_indexmap_init() and is not changed
- * after that.
- * Hence, it cannot be directly applied to the index group passed to
- * gmx_ana_indexmap_update() unless \p bMaskOnly was specified or the
- * index group is identical to the one provided to gmx_ana_indexmap_init().
- */
- t_blocka b;
- /*! \brief
- * TRUE if the current reference IDs are for the whole group (internal use only).
- *
- * This is used internally to optimize the evaluation such that
- * gmx_ana_indexmap_update() does not take any time if the group is
- * actually static.
- */
- gmx_bool bStatic;
- /*! \brief
- * TRUE if the current mapping is for the whole group (internal use only).
- *
- * This is used internally to optimize the evaluation such that
- * gmx_ana_indexmap_update() does not take any time if the group is
- * actually static.
- */
- gmx_bool bMapStatic;
-} gmx_ana_indexmap_t;
-
-
-/*! \name Functions for handling gmx_ana_indexgrps_t
- */
-/*@{*/
-/** Allocate memory for index groups. */
-void
-gmx_ana_indexgrps_alloc(gmx_ana_indexgrps_t **g, int ngrps);
-/** Initializes index groups from arrays. */
-void
-gmx_ana_indexgrps_set(gmx_ana_indexgrps_t **g, int ngrps, int *isize,
- atom_id **index, char **name, gmx_bool bFree);
-/** Reads index groups from a file or constructs them from topology. */
-void
-gmx_ana_indexgrps_init(gmx_ana_indexgrps_t **g, t_topology *top,
- const char *fnm);
-/** Ask user to select index groups, possibly constructing groups from
- * topology. */
-void
-gmx_ana_indexgrps_get(gmx_ana_indexgrps_t **g, t_topology *top,
- const char *fnm, int ngrps);
-/** Ask user to select index groups from those specified in a file. */
-void
-gmx_ana_indexgrps_rd(gmx_ana_indexgrps_t **g, const char *fnm, int ngrps);
-/** Frees memory allocated for index groups. */
-void
-gmx_ana_indexgrps_free(gmx_ana_indexgrps_t *g);
-/** Create a deep copy of \c gmx_ana_indexgrps_t. */
-void
-gmx_ana_indexgrps_clone(gmx_ana_indexgrps_t **dest, gmx_ana_indexgrps_t *src);
-/** Returns TRUE if the index group structure is emtpy. */
-gmx_bool
-gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t *g);
-
-/** Returns a pointer to an index group. */
-gmx_ana_index_t *
-gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t *g, int n);
-/** Extracts a single index group. */
-gmx_bool
-gmx_ana_indexgrps_extract(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, int n);
-/** Finds and extracts a single index group by name. */
-gmx_bool
-gmx_ana_indexgrps_find(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, char *name);
-
-/** Writes out a list of index groups. */
-void
-gmx_ana_indexgrps_print(gmx_ana_indexgrps_t *g, int maxn);
-/*@}*/
-
-/*! \name Functions for handling gmx_ana_index_t
- */
-/*@{*/
-/** Reserves memory to store an index group of size \p isize. */
-void
-gmx_ana_index_reserve(gmx_ana_index_t *g, int isize);
-/** Frees any memory not necessary to hold the current contents. */
-void
-gmx_ana_index_squeeze(gmx_ana_index_t *g);
-/** Initializes an empty index group. */
-void
-gmx_ana_index_clear(gmx_ana_index_t *g);
-/** Constructs a \c gmx_ana_index_t from given values. */
-void
-gmx_ana_index_set(gmx_ana_index_t *g, int isize, atom_id *index, char *name,
- int nalloc);
-/** Creates a simple index group from the first to the \p natoms'th atom. */
-void
-gmx_ana_index_init_simple(gmx_ana_index_t *g, int natoms, char *name);
-/** Frees memory allocated for an index group. */
-void
-gmx_ana_index_deinit(gmx_ana_index_t *g);
-/** Copies a \c gmx_ana_index_t. */
-void
-gmx_ana_index_copy(gmx_ana_index_t *dest, gmx_ana_index_t *src, gmx_bool bAlloc);
-
-/** Writes out the contents of a index group. */
-void
-gmx_ana_index_dump(gmx_ana_index_t *g, int i, int maxn);
-
-/** Checks whether all indices are between 0 and \p natoms. */
-void
-gmx_ana_index_check(gmx_ana_index_t *g, int natoms);
-/** Checks whether an index group is sorted. */
-gmx_bool
-gmx_ana_index_check_sorted(gmx_ana_index_t *g);
-/*@}*/
-
-/*! \name Functions for set operations on gmx_ana_index_t
- */
-/*@{*/
-/** Sorts the indices within an index group. */
-void
-gmx_ana_index_sort(gmx_ana_index_t *g);
-/** Checks whether two index groups are equal. */
-gmx_bool
-gmx_ana_index_equals(gmx_ana_index_t *a, gmx_ana_index_t *b);
-/** Checks whether a sorted index group contains another sorted index group. */
-gmx_bool
-gmx_ana_index_contains(gmx_ana_index_t *a, gmx_ana_index_t *b);
-
-/** Calculates the intersection between two sorted index groups. */
-void
-gmx_ana_index_intersection(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b);
-/** Calculates the set difference between two sorted index groups. */
-void
-gmx_ana_index_difference(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b);
-/** Calculates the size of the difference between two sorted index groups. */
-int
-gmx_ana_index_difference_size(gmx_ana_index_t *a, gmx_ana_index_t *b);
-/** Calculates the union of two sorted index groups. */
-void
-gmx_ana_index_union(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b);
-/** Merges two distinct sorted index groups. */
-void
-gmx_ana_index_merge(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b);
-/** Calculates the intersection and the difference in one call. */
-void
-gmx_ana_index_partition(gmx_ana_index_t *dest1, gmx_ana_index_t *dest2,
- gmx_ana_index_t *src, gmx_ana_index_t *g);
-/*@}*/
-
-/*! \name Functions for handling gmx_ana_indexmap_t and related things
- */
-/*@{*/
-/** Partition a group based on topology information. */
-void
-gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g,
- e_index_t type, gmx_bool bComplete);
-/** Checks whether a group consists of full blocks. */
-gmx_bool
-gmx_ana_index_has_full_blocks(gmx_ana_index_t *g, t_block *b);
-/** Checks whether a group consists of full blocks. */
-gmx_bool
-gmx_ana_index_has_full_ablocks(gmx_ana_index_t *g, t_blocka *b);
-/** Checks whether a group consists of full residues/molecules. */
-gmx_bool
-gmx_ana_index_has_complete_elems(gmx_ana_index_t *g, e_index_t type, t_topology *top);
-
-/** Initializes an empty index group mapping. */
-void
-gmx_ana_indexmap_clear(gmx_ana_indexmap_t *m);
-/** Reserves memory for an index group mapping. */
-void
-gmx_ana_indexmap_reserve(gmx_ana_indexmap_t *m, int nr, int isize);
-/** Initializes an index group mapping. */
-void
-gmx_ana_indexmap_init(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
- t_topology *top, e_index_t type);
-/** Sets an index group mapping to be static. */
-void
-gmx_ana_indexmap_set_static(gmx_ana_indexmap_t *m, t_blocka *b);
-/** Frees memory allocated for index group mapping. */
-void
-gmx_ana_indexmap_deinit(gmx_ana_indexmap_t *m);
-/** Makes a deep copy of an index group mapping. */
-void
-gmx_ana_indexmap_copy(gmx_ana_indexmap_t *dest, gmx_ana_indexmap_t *src, gmx_bool bFirst);
-/** Updates an index group mapping. */
-void
-gmx_ana_indexmap_update(gmx_ana_indexmap_t *m, gmx_ana_index_t *g, gmx_bool bMaskOnly);
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gromacs Runs On Most of All Computer Systems
- */
-
-#ifndef _mdrun_h
-#define _mdrun_h
-
-#include <stdio.h>
-#include <time.h>
-#include "typedefs.h"
-#include "network.h"
-#include "tgroup.h"
-#include "filenm.h"
-#include "mshift.h"
-#include "force.h"
-#include "edsam.h"
-#include "mdebin.h"
-#include "vcm.h"
-#include "vsite.h"
-#include "pull.h"
-#include "update.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define MD_POLARISE (1<<2)
-#define MD_IONIZE (1<<3)
-#define MD_RERUN (1<<4)
-#define MD_RERUN_VSITE (1<<5)
-#define MD_FFSCAN (1<<6)
-#define MD_SEPPOT (1<<7)
-#define MD_PARTDEC (1<<9)
-#define MD_DDBONDCHECK (1<<10)
-#define MD_DDBONDCOMM (1<<11)
-#define MD_CONFOUT (1<<12)
-#define MD_REPRODUCIBLE (1<<13)
-#define MD_READ_RNG (1<<14)
-#define MD_APPENDFILES (1<<15)
-#define MD_KEEPANDNUMCPT (1<<16)
-#define MD_READ_EKIN (1<<17)
-#define MD_STARTFROMCPT (1<<18)
-#define MD_RESETCOUNTERSHALFWAY (1<<19)
-
-/* Define a number of flags to better control the information
- * passed to compute_globals in md.c and global_stat.
- */
-
-/* We are rerunning the simulation */
-#define CGLO_RERUNMD (1<<1)
-/* we are computing the kinetic energy from average velocities */
-#define CGLO_EKINAVEVEL (1<<2)
-/* we are removing the center of mass momenta */
-#define CGLO_STOPCM (1<<3)
-/* bGStat is defined in do_md */
-#define CGLO_GSTAT (1<<4)
-/* Sum the energy terms in global computation */
-#define CGLO_ENERGY (1<<6)
-/* Sum the kinetic energy terms in global computation */
-#define CGLO_TEMPERATURE (1<<7)
-/* Sum the kinetic energy terms in global computation */
-#define CGLO_PRESSURE (1<<8)
-/* Sum the constraint term in global computation */
-#define CGLO_CONSTRAINT (1<<9)
-/* we are using an integrator that requires iteration over some steps - currently not used*/
-#define CGLO_ITERATE (1<<10)
-/* it is the first time we are iterating (or, only once through is required */
-#define CGLO_FIRSTITERATE (1<<11)
-/* Reading ekin from the trajectory */
-#define CGLO_READEKIN (1<<12)
-/* we need to reset the ekin rescaling factor here */
-#define CGLO_SCALEEKIN (1<<13)
-
-enum {
- ddnoSEL, ddnoINTERLEAVE, ddnoPP_PME, ddnoCARTESIAN, ddnoNR
-};
-
-typedef struct {
- double real;
-#ifdef GMX_CRAY_XT3
- double proc;
-#else
- clock_t proc;
-#endif
- double realtime;
- double proctime;
- double time_per_step;
- double last;
- gmx_large_int_t nsteps_done;
-} gmx_runtime_t;
-
-typedef struct {
- t_fileio *fp_trn;
- t_fileio *fp_xtc;
- int xtc_prec;
- ener_file_t fp_ene;
- const char *fn_cpt;
- gmx_bool bKeepAndNumCPT;
- int eIntegrator;
- int simulation_part;
- FILE *fp_dhdl;
- FILE *fp_field;
-} gmx_mdoutf_t;
-
-/* Variables for temporary use with the deform option,
- * used in runner.c and md.c.
- * (These variables should be stored in the tpx file.)
- */
-extern gmx_large_int_t deform_init_init_step_tpx;
-extern matrix deform_init_box_tpx;
-#ifdef GMX_THREADS
-extern tMPI_Thread_mutex_t deform_init_box_mutex;
-
-/* The minimum number of atoms per thread. With fewer atoms than this,
- * the number of threads will get lowered.
- */
-#define MIN_ATOMS_PER_THREAD 90
-#endif
-
-
-typedef double gmx_integrator_t(FILE *log,t_commrec *cr,
- int nfile,const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,
- gmx_bool bCompact, int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,
- t_inputrec *inputrec,
- gmx_mtop_t *mtop,t_fcdata *fcd,
- t_state *state,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,
- t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime);
-
-typedef struct gmx_global_stat *gmx_global_stat_t;
-
-/* ROUTINES from md.c */
-
-gmx_integrator_t do_md;
-
-gmx_integrator_t do_md_openmm;
-
-/* ROUTINES from minimize.c */
-
-gmx_integrator_t do_steep;
-/* Do steepest descents EM */
-
-gmx_integrator_t do_cg;
-/* Do conjugate gradient EM */
-
-gmx_integrator_t do_lbfgs;
-/* Do conjugate gradient L-BFGS */
-
-gmx_integrator_t do_nm;
-/* Do normal mode analysis */
-
-/* ROUTINES from tpi.c */
-
-gmx_integrator_t do_tpi;
-/* Do test particle insertion */
-
-
-/* ROUTINES from sim_util.c */
-void do_pbc_first(FILE *log,matrix box,t_forcerec *fr,
- t_graph *graph,rvec x[]);
-
-void do_pbc_first_mtop(FILE *fplog,int ePBC,matrix box,
- gmx_mtop_t *mtop,rvec x[]);
-
-void do_pbc_mtop(FILE *fplog,int ePBC,matrix box,
- gmx_mtop_t *mtop,rvec x[]);
-
-
-/* ROUTINES from stat.c */
-gmx_global_stat_t global_stat_init(t_inputrec *ir);
-
-void global_stat_destroy(gmx_global_stat_t gs);
-
-void global_stat(FILE *log,gmx_global_stat_t gs,
- t_commrec *cr,gmx_enerdata_t *enerd,
- tensor fvir,tensor svir,rvec mu_tot,
- t_inputrec *inputrec,
- gmx_ekindata_t *ekind,
- gmx_constr_t constr,t_vcm *vcm,
- int nsig,real *sig,
- gmx_mtop_t *top_global, t_state *state_local,
- gmx_bool bSumEkinhOld, int flags);
-/* Communicate statistics over cr->mpi_comm_mysim */
-
-gmx_mdoutf_t *init_mdoutf(int nfile,const t_filenm fnm[],
- int mdrun_flags,
- const t_commrec *cr,const t_inputrec *ir,
- const output_env_t oenv);
-/* Returns a pointer to a data structure with all output file pointers
- * and names required by mdrun.
- */
-
-void done_mdoutf(gmx_mdoutf_t *of);
-/* Close all open output files and free the of pointer */
-
-#define MDOF_X (1<<0)
-#define MDOF_V (1<<1)
-#define MDOF_F (1<<2)
-#define MDOF_XTC (1<<3)
-#define MDOF_CPT (1<<4)
-
-void write_traj(FILE *fplog,t_commrec *cr,
- gmx_mdoutf_t *of,
- int mdof_flags,
- gmx_mtop_t *top_global,
- gmx_large_int_t step,double t,
- t_state *state_local,t_state *state_global,
- rvec *f_local,rvec *f_global,
- int *n_xtc,rvec **x_xtc);
-/* Routine that writes frames to trn, xtc and/or checkpoint.
- * What is written is determined by the mdof_flags defined above.
- * Data is collected to the master node only when necessary.
- */
-
-int do_per_step(gmx_large_int_t step,gmx_large_int_t nstep);
-/* Return TRUE if io should be done */
-
-int do_any_io(int step, t_inputrec *ir);
-
-/* ROUTINES from sim_util.c */
-
-double gmx_gettime();
-
-void print_time(FILE *out, gmx_runtime_t *runtime,
- gmx_large_int_t step,t_inputrec *ir, t_commrec *cr);
-
-void runtime_start(gmx_runtime_t *runtime);
-
-void runtime_end(gmx_runtime_t *runtime);
-
-void runtime_upd_proc(gmx_runtime_t *runtime);
-/* The processor time should be updated every once in a while,
- * since on 32-bit manchines it loops after 72 minutes.
- */
-
-void print_date_and_time(FILE *log,int pid,const char *title,
- const gmx_runtime_t *runtime);
-
-void nstop_cm(FILE *log,t_commrec *cr,
- int start,int nr_atoms,real mass[],rvec x[],rvec v[]);
-
-void finish_run(FILE *log,t_commrec *cr,const char *confout,
- t_inputrec *inputrec,
- t_nrnb nrnb[],gmx_wallcycle_t wcycle,
- gmx_runtime_t *runtime,
- gmx_bool bWriteStat);
-
-void calc_enervirdiff(FILE *fplog,int eDispCorr,t_forcerec *fr);
-
-void calc_dispcorr(FILE *fplog,t_inputrec *ir,t_forcerec *fr,
- gmx_large_int_t step, int natoms,
- matrix box,real lambda,tensor pres,tensor virial,
- real *prescorr, real *enercorr, real *dvdlcorr);
-
-typedef enum
-{
- LIST_SCALARS =0001,
- LIST_INPUTREC =0002,
- LIST_TOP =0004,
- LIST_X =0010,
- LIST_V =0020,
- LIST_F =0040,
- LIST_LOAD =0100
-} t_listitem;
-
-void check_nnodes_top(char *fn,t_topology *top);
-/* Reset the tpr file to work with one node if necessary */
-
-
-/* check the version */
-void check_ir_old_tpx_versions(t_commrec *cr,FILE *fplog,
- t_inputrec *ir,gmx_mtop_t *mtop);
-
-/* Allocate and initialize node-local state entries. */
-void set_state_entries(t_state *state,const t_inputrec *ir,int nnodes);
-
-/* Broadcast the data for a simulation, and allocate node-specific settings
- such as rng generators. */
-void init_parallel(FILE *log, t_commrec *cr, t_inputrec *inputrec,
- gmx_mtop_t *mtop);
-
-
-void do_constrain_first(FILE *log,gmx_constr_t constr,
- t_inputrec *inputrec,t_mdatoms *md,
- t_state *state,rvec *f,
- t_graph *graph,t_commrec *cr,t_nrnb *nrnb,
- t_forcerec *fr, gmx_localtop_t *top, tensor shake_vir);
-
-void dynamic_load_balancing(gmx_bool bVerbose,t_commrec *cr,real capacity[],
- int dimension,t_mdatoms *md,t_topology *top,
- rvec x[],rvec v[],matrix box);
-/* Perform load balancing, i.e. split the particles over processors
- * based on their coordinates in the "dimension" direction.
- */
-
-int mdrunner(int nthreads_requested, FILE *fplog,t_commrec *cr,int nfile,
- const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
- gmx_bool bCompact, int nstglobalcomm, ivec ddxyz,int dd_node_order,
- real rdd, real rconstr, const char *dddlb_opt,real dlb_scale,
- const char *ddcsx,const char *ddcsy,const char *ddcsz,
- int nstepout, int resetstep, int nmultisim, int repl_ex_nst,
- int repl_ex_seed, real pforce,real cpt_period,real max_hours,
- const char *deviceOptions, unsigned long Flags);
-/* Driver routine, that calls the different methods */
-
-void md_print_warning(const t_commrec *cr,FILE *fplog,const char *buf);
-/* Print a warning message to stderr on the master node
- * and to fplog if fplog!=NULL.
- */
-
-void init_md(FILE *fplog,
- t_commrec *cr,t_inputrec *ir, const output_env_t oenv,
- double *t,double *t0,
- real *lambda,double *lam0,
- t_nrnb *nrnb,gmx_mtop_t *mtop,
- gmx_update_t *upd,
- int nfile,const t_filenm fnm[],
- gmx_mdoutf_t **outf,t_mdebin **mdebin,
- tensor force_vir,tensor shake_vir,
- rvec mu_tot,
- gmx_bool *bSimAnn,t_vcm **vcm,
- t_state *state, unsigned long Flags);
- /* Routine in sim_util.c */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _mdrun_h */
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2008, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
- */
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* define USE_MPE if you want MPE logging
- *
- * you then need to link with the appropriate libraries
- * that come with the mpich distribution (can be found in the
- * mpe subdirectory */
-/* #define USE_MPE */
-
-/* define BARRIERS if you want to have extra MPI_Barriers
- * in the code which might help analyzing the MPE logfiles
- */
-/* #define BARRIERS */
-#ifdef BARRIERS
-#define GMX_BARRIER(communicator) MPI_Barrier(communicator)
-#else
-#define GMX_BARRIER(communicator)
-#endif
-
-#ifdef USE_MPE
-#define GMX_MPE_LOG(event) MPE_Log_event(event, 0, "")
-#else
-#define GMX_MPE_LOG(event)
-#endif
-
-#ifdef USE_MPE
-#include <mpe.h>
- /* Define MPE logging events here */
- /* General events */
- int ev_timestep1, ev_timestep2;
- int ev_ns_start, ev_ns_finish;
- int ev_calc_bonds_start, ev_calc_bonds_finish;
- int ev_send_coordinates_start, ev_send_coordinates_finish;
- int ev_update_fr_start, ev_update_fr_finish;
- int ev_clear_rvecs_start, ev_clear_rvecs_finish;
- int ev_output_start, ev_output_finish;
- int ev_update_start, ev_update_finish;
- int ev_force_start, ev_force_finish;
- int ev_do_fnbf_start, ev_do_fnbf_finish;
-
- /* Shift related events*/
- int ev_shift_start, ev_shift_finish;
- int ev_unshift_start, ev_unshift_finish;
- int ev_mk_mshift_start, ev_mk_mshift_finish;
-
- /* PME related events */
- int ev_pme_start, ev_pme_finish;
- int ev_spread_on_grid_start, ev_spread_on_grid_finish;
- int ev_sum_qgrid_start, ev_sum_qgrid_finish;
- int ev_gmxfft3d_start, ev_gmxfft3d_finish;
- int ev_solve_pme_start, ev_solve_pme_finish;
- int ev_gather_f_bsplines_start, ev_gather_f_bsplines_finish;
- int ev_reduce_start, ev_reduce_finish;
- int ev_rscatter_start, ev_rscatter_finish;
- int ev_alltoall_start, ev_alltoall_finish;
- int ev_pmeredist_start, ev_pmeredist_finish;
- int ev_init_pme_start, ev_init_pme_finish;
- int ev_global_stat_start, ev_global_stat_finish;
- int ev_sum_lrforces_start, ev_sum_lrforces_finish;
- int ev_virial_start, ev_virial_finish;
- int ev_sort_start, ev_sort_finish;
- int ev_sum_qgrid_start, ev_sum_qgrid_finish;
-
- /* Essential dynamics related events */
- int ev_edsam_start, ev_edsam_finish;
- int ev_get_group_x_start, ev_get_group_x_finish;
- int ev_ed_apply_cons_start, ev_ed_apply_cons_finish;
- int ev_fit_to_reference_start, ev_fit_to_reference_finish;
-#endif
-
-#ifdef __cplusplus
-}
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gromacs Runs On Most of All Computer Systems
- */
-
-#ifndef _names_h
-#define _names_h
-
-
-#include "typedefs.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* All string arrays are NULL terminated, and therefore have an
- * extra argument (the +1)
- * these should correspond to names.c and include/types/enums.h
- */
-extern const char *epbc_names[epbcNR+1];
-extern const char *etcoupl_names[etcNR+1];
-extern const char *epcoupl_names[epcNR+1];
-extern const char *epcoupltype_names[epctNR+1];
-extern const char *erefscaling_names[erscNR+1];
-extern const char *ens_names[ensNR+1];
-extern const char *ei_names[eiNR+1];
-extern const char *yesno_names[BOOL_NR+1];
-extern const char *bool_names[BOOL_NR+1];
-extern const char *eel_names[eelNR+1];
-extern const char *eewg_names[eewgNR+1];
-extern const char *evdw_names[evdwNR+1];
-extern const char *econstr_names[econtNR+1];
-extern const char *ptype_str[eptNR+1];
-extern const char *egrp_nm[egNR+1];
-extern const char *edisre_names[edrNR+1];
-extern const char *edisreweighting_names[edrwNR+1];
-extern const char *enbf_names[eNBF_NR+1];
-extern const char *ecomb_names[eCOMB_NR+1];
-extern const char *gtypes[egcNR+1];
-extern const char *efep_names[efepNR+1];
-extern const char *separate_dhdl_file_names[efepNR+1];
-extern const char *dhdl_derivatives_names[efepNR+1];
-extern const char *efep_names[efepNR+1];
-extern const char *esol_names[esolNR+1];
-extern const char *enlist_names[enlistNR+1];
-extern const char *edispc_names[edispcNR+1];
-extern const char *ecm_names[ecmNR+1];
-extern const char *eann_names[eannNR+1];
-extern const char *egb_names[egbNR+1];
-extern const char *eis_names[eisNR+1];
-extern const char *esa_names[esaNR+1];
-extern const char *ewt_names[ewtNR+1];
-extern const char *epull_names[epullNR+1];
-extern const char *epullg_names[epullgNR+1];
-extern const char *eQMmethod_names[eQMmethodNR+1];
-extern const char *eQMbasis_names[eQMbasisNR+1];
-extern const char *eQMMMscheme_names[eQMMMschemeNR+1];
-extern const char *eMultentOpt_names[eMultentOptNR+1];
-
-#define UNDEFINED "UNDEFINED"
-#define ENUM_NAME(e,max,names) ((((e)<0)||((e)>=(max)))?UNDEFINED:(names)[e])
-
-#define BOOL(e) ENUM_NAME(e,BOOL_NR,bool_names)
-#define ENS(e) ENUM_NAME(e,ensNR,ens_names)
-#define EI(e) ENUM_NAME(e,eiNR,ei_names)
-#define EPBC(e) ENUM_NAME(e,epbcNR,epbc_names)
-#define ETCOUPLTYPE(e) ENUM_NAME(e,etcNR,etcoupl_names)
-#define EPCOUPLTYPE(e) ENUM_NAME(e,epcNR,epcoupl_names)
-#define EPCOUPLTYPETYPE(e) ENUM_NAME(e,epctNR,epcoupltype_names)
-#define EREFSCALINGTYPE(e) ENUM_NAME(e,erscNR,erefscaling_names)
-#define EBLOCKS(e) ENUM_NAME(e,ebNR,eblock_names)
-#define EPARAM(e) ENUM_NAME(e,epNR,eparam_names)
-#define EELTYPE(e) ENUM_NAME(e,eelNR,eel_names)
-#define EVDWTYPE(e) ENUM_NAME(e,evdwNR,evdw_names)
-#define ECONSTRTYPE(e) ENUM_NAME(e,econtNR,econstr_names)
-#define EDISRETYPE(e) ENUM_NAME(e,edrNR,edisre_names)
-#define EDISREWEIGHTING(e) ENUM_NAME(e,edrwNR,edisreweighting_names)
-#define ENBFNAME(e) ENUM_NAME(e,eNBF_NR,enbf_names)
-#define ECOMBNAME(e) ENUM_NAME(e,eCOMB_NR,ecomb_names)
-#define EFEPTYPE(e) ENUM_NAME(e,efepNR,efep_names)
-#define SEPDHDLFILETYPE(e) ENUM_NAME(e,sepdhdlfileNR,separate_dhdl_file_names)
-#define DHDLDERIVATIVESTYPE(e) ENUM_NAME(e,dhdlderivativesNR,dhdl_derivatives_names)
-#define ESOLTYPE(e) ENUM_NAME(e,esolNR,esol_names)
-#define ENLISTTYPE(e) ENUM_NAME(e,enlistNR,enlist_names)
-#define EDISPCORR(e) ENUM_NAME(e,edispcNR,edispc_names)
-#define ECOM(e) ENUM_NAME(e,ecmNR,ecm_names)
-#define EANNEAL(e) ENUM_NAME(e,eannNR,eann_names)
-#define EGBALGORITHM(e) ENUM_NAME(e,egbNR,egb_names)
-#define ESAALGORITHM(e) ENUM_NAME(e,esaNR,esa_names)
-#define EIMPLICITSOL(e) ENUM_NAME(e,eisNR,eis_names)
-#define EWALLTYPE(e) ENUM_NAME(e,ewtNR,ewt_names)
-#define EPULLTYPE(e) ENUM_NAME(e,epullNR,epull_names)
-#define EPULLGEOM(e) ENUM_NAME(e,epullgNR,epullg_names)
-#define EQMMETHOD(e) ENUM_NAME(e,eQMmethodNR,eQMmethod_names)
-#define EQMBASIS(e) ENUM_NAME(e,eQMbasisNR,eQMbasis_names)
-#define EQMMMSCHEME(e) ENUM_NAME(e,eQMMMschemeNR,eQMMMscheme_names)
-#define EMULTENTOPT(e) ENUM_NAME(e,eMultentOptNR,eMultentOpt_names)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _names_h */
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for neighborhood searching.
- *
- * The API is documented in more detail on a separate page:
- * \ref nbsearch
- *
- * The functions within this file can be used independently of the other parts
- * of the library.
- * The library also uses the functions internally.
- */
-#ifndef NBSEARCH_H
-#define NBSEARCH_H
-
-#include "typedefs.h"
-
-#include "indexutil.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct gmx_ana_pos_t;
-
-/** Data structure for neighborhood searches. */
-typedef struct gmx_ana_nbsearch_t gmx_ana_nbsearch_t;
-
-/** Create a new neighborhood search data structure. */
-int
-gmx_ana_nbsearch_create(gmx_ana_nbsearch_t **d, real cutoff, int maxn);
-/** Free memory allocated for neighborhood search. */
-void
-gmx_ana_nbsearch_free(gmx_ana_nbsearch_t *d);
-
-/** Initializes neighborhood search for a new frame. */
-int
-gmx_ana_nbsearch_init(gmx_ana_nbsearch_t *d, t_pbc *pbc, int n, rvec x[]);
-/** Initializes neighborhood search for a frame using \c gmx_ana_pos_t. */
-int
-gmx_ana_nbsearch_pos_init(gmx_ana_nbsearch_t *d, t_pbc *pbc,
- struct gmx_ana_pos_t *p);
-/** Sets the exclusions for the next neighborhood search. */
-int
-gmx_ana_nbsearch_set_excl(gmx_ana_nbsearch_t *d, int nexcl, int excl[]);
-/** Check whether a point is within a neighborhood. */
-gmx_bool
-gmx_ana_nbsearch_is_within(gmx_ana_nbsearch_t *d, rvec x);
-/** Check whether a position is within a neighborhood. */
-gmx_bool
-gmx_ana_nbsearch_pos_is_within(gmx_ana_nbsearch_t *d,
- struct gmx_ana_pos_t *p, int i);
-/** Calculates the minimun distance from the reference points. */
-real
-gmx_ana_nbsearch_mindist(gmx_ana_nbsearch_t *d, rvec x);
-/** Calculates the minimun distance from the reference points. */
-real
-gmx_ana_nbsearch_pos_mindist(gmx_ana_nbsearch_t *d,
- struct gmx_ana_pos_t *p, int i);
-/** Finds the first reference position within the cutoff. */
-gmx_bool
-gmx_ana_nbsearch_first_within(gmx_ana_nbsearch_t *d, rvec x, int *jp);
-/** Finds the first reference position within the cutoff. */
-gmx_bool
-gmx_ana_nbsearch_pos_first_within(gmx_ana_nbsearch_t *d,
- struct gmx_ana_pos_t *p, int i, int *jp);
-/** Finds the next reference position within the cutoff. */
-gmx_bool
-gmx_ana_nbsearch_next_within(gmx_ana_nbsearch_t *d, int *jp);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gromacs Runs On Most of All Computer Systems
- */
-
-#ifndef _physics_h
-#define _physics_h
-
-/*
- * Physical constants to be used in Gromacs.
- * No constants (apart from 0, 1 or 2) should
- * be anywhere else in the code.
- */
-
-#include <math.h>
-
-/* we do it anyway. */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef M_PI
-#ifdef _PI
-#define M_PI _PI
-#else
-#define M_PI 3.14159265358979323846
-#endif
-#endif
-
-#define ANGSTROM (1e-10) /* Old... */
-#define KILO (1e3) /* Thousand */
-#define NANO (1e-9) /* A Number */
-#define PICO (1e-12) /* A Number */
-#define A2NM (ANGSTROM/NANO) /* NANO */
-#define NM2A (NANO/ANGSTROM) /* 10.0 */
-#define RAD2DEG (180.0/M_PI) /* Conversion */
-#define DEG2RAD (M_PI/180.0) /* id */
-#define CAL2JOULE (4.184) /* id */
-#define E_CHARGE (1.60217733e-19) /* Coulomb */
-
-#define AMU (1.6605402e-27) /* kg */
-#define BOLTZMANN (1.380658e-23) /* (J/K) */
-#define AVOGADRO (6.0221367e23) /* () */
-#define RGAS (BOLTZMANN*AVOGADRO) /* (J/(mol K)) */
-#define BOLTZ (RGAS/KILO) /* (kJ/(mol K)) */
-#define FARADAY (E_CHARGE*AVOGADRO) /* (C/mol) */
-#define ELECTRONVOLT (E_CHARGE*AVOGADRO/KILO) /* (kJ/mol) */
-#define PLANCK1 (6.6262e-34) /* J s */
-#define PLANCK (6.6262e-34*AVOGADRO/(PICO*KILO)) /* (kJ/mol) ps */
-
-#define EPSILON0 (5.72765E-4) /* (e^2 / Na (kJ nm))
- == (e^2 mol/(kJ nm)) */
-
-#define SPEED_OF_LIGHT (2.9979245800E05) /* nm/ps */
-#define ATOMICMASS_keV (940000.0) /* Atomic mass in keV */
-#define ELECTRONMASS_keV (512.0) /* Electron mas in keV */
-
-/* Improved accuracy (PL & EL, 20090421) */
-#define FACEL (332.0636930*CAL2JOULE)/* (10 * (ONE_4PI_EPS0)) */
-#define ONE_4PI_EPS0 (FACEL*0.1) /* 1/(4*pi*e0)*/
-#define PRESFAC (16.6054) /* bar / pressure unity */
-#define ENM2DEBYE 48.0321 /* Convert electron nm *
- * to debye */
-#define DEBYE2ENM 0.02081941
-/* to convert from a acceleration in (e V)/(amu nm) */
-/* FIELDFAC is also Faraday's constant and E_CHARGE/(1e6 AMU) */
-#define FIELDFAC (FARADAY/KILO)
-
-/* to convert AU to MD units: */
-#define HARTREE2KJ 4.3597482e-21
-#define BOHR2NM 0.0529177249
-#define HARTREE_BOHR2MD (HARTREE2KJ*AVOGADRO/BOHR2NM)
-
-
-/* The four basic units */
-#define unit_length "nm"
-#define unit_time "ps"
-#define unit_mass "u"
-#define unit_energy "kJ/mol"
-
-/* Temperature unit, T in this unit times BOLTZ give energy in unit_energy */
-#define unit_temp_K "K"
-
-/* Charge unit, electron charge, involves ONE_4PI_EPS0 */
-#define unit_charge_e "e"
-
-/* Pressure unit, pressure in basic units times PRESFAC gives this unit */
-#define unit_pres_bar "bar"
-
-/* Dipole unit, debye, conversion from the unit_charge_e involves ENM2DEBYE */
-#define unit_dipole_D "D"
-
-/* Derived units from basic units only */
-#define unit_vel unit_length "/" unit_time
-#define unit_volume unit_length "^3"
-#define unit_invtime "1/" unit_time
-
-/* Other derived units */
-#define unit_surft_bar unit_pres_bar " " unit_length
-
-/* SI units, conversion from basic units involves NANO, PICO and AMU */
-#define unit_length_SI "m"
-#define unit_time_SI "s"
-#define unit_mass_SI "kg"
-
-#define unit_density_SI unit_mass_SI "/" unit_length_SI "^3"
-#define unit_invvisc_SI unit_length_SI " " unit_time_SI "/" unit_mass_SI
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* _physics_h */
-
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for structured and optimized calculation of positions.
- *
- * The functions in this header are used internally by the analysis library
- * to calculate positions.
- * They can also be used in user code, but in most cases there should be no
- * need. Instead, one should write an analysis tool such that it gets all
- * positions through selections.
- *
- * \internal
- *
- * The API is documented in more detail on a separate page:
- * \ref poscalcengine.
- */
-#ifndef POSCALC_H
-#define POSCALC_H
-
-#include "typedefs.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*! \name Flags for position calculation.
- * \anchor poscalc_flags
- */
-/*@{*/
-/*! \brief
- * Use mass weighting.
- *
- * If this flag is set, the positions will be calculated using mass weighting,
- * i.e., one gets center-of-mass positions.
- * Without the flag, center-of-geometry positions are calculated.
- * Does not have any effect if the calculation type is \ref POS_ATOM.
- */
-#define POS_MASS 1
-/*! \brief
- * Calculate positions for the same atoms in residues/molecules.
- *
- * If this flag is set, the positions are always calculated using the same
- * atoms for each residue/molecule, even if the evaluation group contains only
- * some of the atoms for some frames.
- * The group passed to gmx_ana_poscalc_set_maxindex() is used to determine
- * the atoms to use for the calculation.
- *
- * Has no effect unless \ref POS_DYNAMIC is set or if the calculation type
- * is not \ref POS_RES of \ref POS_MOL.
- */
-#define POS_COMPLMAX 2
-/*! \brief
- * Calculate positions for whole residues/molecules.
- *
- * If this flag is set, the positions will be calculated for whole
- * residues/molecules, even if the group contains only some of the atoms in
- * the residue/molecule.
- *
- * Has no effect unless the calculation type is \ref POS_RES or \ref POS_MOL.
- */
-#define POS_COMPLWHOLE 4
-/*! \brief
- * Enable handling of changing calculation groups.
- *
- * Can be used for static calculations as well, but implies a small
- * performance penalty.
- */
-#define POS_DYNAMIC 16
-/*! \brief
- * Update \c gmx_ana_pos_t::m dynamically for an otherwise static
- * calculation.
- *
- * Has effect only if \ref POS_DYNAMIC is not set.
- */
-#define POS_MASKONLY 32
-/*! \brief
- * Calculate velocities of the positions.
- */
-#define POS_VELOCITIES 64
-/*! \brief
- * Calculate forces on the positions.
- */
-#define POS_FORCES 128
-/*@}*/
-
-/** Specifies the type of positions to be calculated. */
-typedef enum
-{
- POS_ATOM, /**< Copy atomic coordinates. */
- POS_RES, /**< Calculate center for each residue. */
- POS_MOL, /**< Calculate center for each molecule. */
- POS_ALL, /**< Calculate center for the whole group. */
- POS_ALL_PBC /**< Calculate center for the whole group with PBC. */
-} e_poscalc_t;
-
-/** Collection of \c gmx_ana_poscalc_t structures for the same topology. */
-typedef struct gmx_ana_poscalc_coll_t gmx_ana_poscalc_coll_t;
-/** Data structure for position calculation. */
-typedef struct gmx_ana_poscalc_t gmx_ana_poscalc_t;
-
-struct gmx_ana_index_t;
-struct gmx_ana_pos_t;
-
-/** Converts a string to parameters for gmx_ana_poscalc_create(). */
-int
-gmx_ana_poscalc_type_from_enum(const char *post, e_poscalc_t *type, int *flags);
-/** Creates a list of strings for position enum parameter handling. */
-const char **
-gmx_ana_poscalc_create_type_enum(gmx_bool bAtom);
-
-/** Creates a new position calculation collection object. */
-int
-gmx_ana_poscalc_coll_create(gmx_ana_poscalc_coll_t **pccp);
-/** Sets the topology for a position calculation collection. */
-void
-gmx_ana_poscalc_coll_set_topology(gmx_ana_poscalc_coll_t *pcc, t_topology *top);
-/** Frees memory allocated for a position calculation collection. */
-void
-gmx_ana_poscalc_coll_free(gmx_ana_poscalc_coll_t *pcc);
-/** Prints information about calculations in a position calculation collection. */
-void
-gmx_ana_poscalc_coll_print_tree(FILE *fp, gmx_ana_poscalc_coll_t *pcc);
-
-/** Creates a new position calculation. */
-int
-gmx_ana_poscalc_create(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
- e_poscalc_t type, int flags);
-/** Creates a new position calculation based on an enum value. */
-int
-gmx_ana_poscalc_create_enum(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
- const char *post, int flags);
-/** Sets the flags for position calculation. */
-void
-gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t *pc, int flags);
-/** Sets the maximum possible input index group for position calculation. */
-void
-gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t *pc, struct gmx_ana_index_t *g);
-/** Initializes positions for position calculation output. */
-void
-gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t *pc, struct gmx_ana_pos_t *p);
-/** Frees the memory allocated for position calculation. */
-void
-gmx_ana_poscalc_free(gmx_ana_poscalc_t *pc);
-/** Returns TRUE if the position calculation requires topology information. */
-gmx_bool
-gmx_ana_poscalc_requires_top(gmx_ana_poscalc_t *pc);
-
-/** Initializes evaluation for a position calculation collection. */
-void
-gmx_ana_poscalc_init_eval(gmx_ana_poscalc_coll_t *pcc);
-/** Initializes a position calculation collection for a new frame. */
-void
-gmx_ana_poscalc_init_frame(gmx_ana_poscalc_coll_t *pcc);
-/** Updates a single COM/COG structure for a frame. */
-void
-gmx_ana_poscalc_update(gmx_ana_poscalc_t *pc,
- struct gmx_ana_pos_t *p, struct gmx_ana_index_t *g,
- t_trxframe *fr, t_pbc *pbc);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for handling positions.
- */
-#ifndef POSITION_H
-#define POSITION_H
-
-#include "typedefs.h"
-
-#include "indexutil.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*! \brief
- * Stores a set of positions together with their origins.
- */
-typedef struct gmx_ana_pos_t
-{
- /*! \brief
- * Number of positions.
- */
- int nr;
- /*! \brief
- * Array of positions.
- */
- rvec *x;
- /*! \brief
- * Velocities (can be NULL).
- */
- rvec *v;
- /*! \brief
- * Forces (can be NULL).
- */
- rvec *f;
- /*! \brief
- * Mapping of the current positions to the original group.
- *
- * \see gmx_ana_indexmap_t
- */
- gmx_ana_indexmap_t m;
- /*! \brief
- * Pointer to the current evaluation group.
- */
- gmx_ana_index_t *g;
- /*! \brief
- * Number of elements allocated for \c x.
- */
- int nalloc_x;
-} gmx_ana_pos_t;
-
-/** Initializes an empty position structure. */
-void
-gmx_ana_pos_clear(gmx_ana_pos_t *pos);
-/** Ensures that enough memory has been allocated to store positions. */
-void
-gmx_ana_pos_reserve(gmx_ana_pos_t *pos, int n, int isize);
-/** Request memory allocation for velocities. */
-void
-gmx_ana_pos_reserve_velocities(gmx_ana_pos_t *pos);
-/** Request memory allocation for forces. */
-void
-gmx_ana_pos_reserve_forces(gmx_ana_pos_t *pos);
-/** Initializes a \c gmx_ana_pos_t to represent a constant position. */
-void
-gmx_ana_pos_init_const(gmx_ana_pos_t *pos, rvec x);
-/** Frees the memory allocated for position storage. */
-void
-gmx_ana_pos_deinit(gmx_ana_pos_t *pos);
-/** Frees the memory allocated for positions. */
-void
-gmx_ana_pos_free(gmx_ana_pos_t *pos);
-/** Copies the evaluated positions to a preallocated data structure. */
-void
-gmx_ana_pos_copy(gmx_ana_pos_t *dest, gmx_ana_pos_t *src, gmx_bool bFirst);
-
-/** Sets the number of positions in a position structure. */
-void
-gmx_ana_pos_set_nr(gmx_ana_pos_t *pos, int n);
-/** Sets the evaluation group of a position data structure. */
-void
-gmx_ana_pos_set_evalgrp(gmx_ana_pos_t *pos, gmx_ana_index_t *g);
-/** Empties a position data structure with full initialization. */
-void
-gmx_ana_pos_empty_init(gmx_ana_pos_t *pos);
-/** Empties a position data structure. */
-void
-gmx_ana_pos_empty(gmx_ana_pos_t *pos);
-/** Appends a position to a preallocated data structure with full
- * initialization. */
-void
-gmx_ana_pos_append_init(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
- gmx_ana_pos_t *src, int i);
-/** Appends a position to a preallocated data structure. */
-void
-gmx_ana_pos_append(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
- gmx_ana_pos_t *src, int i, int refid);
-/** Updates position data structure state after appends. */
-void
-gmx_ana_pos_append_finish(gmx_ana_pos_t *pos);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for handling selection (the \c gmx_ana_selection_t structure and related functions).
- *
- * There should be no need to use the data structures or call the
- * functions in this file directly unless using the selection routines outside
- * the main trajectory analysis API.
- */
-#ifndef SELECTION_H
-#define SELECTION_H
-
-#include "position.h"
-#include "indexutil.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/** Information for a collection of selections. */
-typedef struct gmx_ana_selcollection_t gmx_ana_selcollection_t;
-
-struct gmx_ana_poscalc_coll_t;
-
-/** Defines the type of covered fraction. */
-typedef enum
-{
- CFRAC_NONE, /**< No covered fraction (everything covered). */
- CFRAC_SOLIDANGLE /**< Fraction of a solid (3D) angle covered. */
-} e_coverfrac_t;
-
-/*! \brief
- * Describes a single selection.
- */
-typedef struct gmx_ana_selection_t
-{
- /** Name of the selection. */
- char *name;
- /** The actual selection string. */
- char *selstr;
- /** Selected positions. */
- gmx_ana_pos_t p;
- /** Masses associated with the positions. */
- real *m;
- /** Charges associated with the positions. */
- real *q;
- /** Pointer to the index group that holds the selected atoms. */
- struct gmx_ana_index_t *g;
- /** TRUE if the value can change as a function of time. */
- gmx_bool bDynamic;
- /** Type of the covered fraction. */
- e_coverfrac_t cfractype;
- /** TRUE if the covered fraction depends on the frame. */
- gmx_bool bCFracDyn;
- /** Covered fraction of the selection for the current frame. */
- real cfrac;
- /** The average covered fraction (over the trajectory). */
- real avecfrac;
-
- /*! \brief
- * Pointer to the root of the selection element tree (internal use only).
- *
- * \internal
- * This field is NULL if the selection has been loaded directly from an
- * index file.
- */
- struct t_selelem *selelem;
- /** Original masses of all possible positions (internal use only). */
- real *orgm;
- /** Original charges of all possible positions (internal use only). */
- real *orgq;
-} gmx_ana_selection_t;
-
-/** Frees the memory allocated for a selection. */
-void
-gmx_ana_selection_free(gmx_ana_selection_t *sel);
-/** Returns the name of a selection. */
-char *
-gmx_ana_selection_name(gmx_ana_selection_t *sel);
-/** Prints out the selection information. */
-void
-gmx_ana_selection_print_info(gmx_ana_selection_t *sel);
-/** Initializes the information for covered fraction. */
-gmx_bool
-gmx_ana_selection_init_coverfrac(gmx_ana_selection_t *sel, e_coverfrac_t type);
-
-/** Creates a new empty selection collection. */
-int
-gmx_ana_selcollection_create(gmx_ana_selcollection_t **sc,
- struct gmx_ana_poscalc_coll_t *pcc);
-/** Frees the memory allocated for a selection collection. */
-void
-gmx_ana_selcollection_free(gmx_ana_selcollection_t *sc);
-/** Sets the default reference position handling for a selection collection. */
-void
-gmx_ana_selcollection_set_refpostype(gmx_ana_selcollection_t *sc, const char *type);
-/** Sets the default output position handling for a selection collection. */
-void
-gmx_ana_selcollection_set_outpostype(gmx_ana_selcollection_t *sc,
- const char *type, gmx_bool bMaskOnly);
-/** Request evaluation of velocities for selections. */
-void
-gmx_ana_selcollection_set_veloutput(gmx_ana_selcollection_t *sc,
- gmx_bool bVelOut);
-/** Request evaluation of forces for selections. */
-void
-gmx_ana_selcollection_set_forceoutput(gmx_ana_selcollection_t *sc,
- gmx_bool bForceOut);
-/** Sets the topology for a selection collection. */
-int
-gmx_ana_selcollection_set_topology(gmx_ana_selcollection_t *sc, t_topology *top,
- int natoms);
-/** Returns the number of selections specified by a selection collection. */
-int
-gmx_ana_selcollection_get_count(gmx_ana_selcollection_t *sc);
-/** Returns a selection by index. */
-gmx_ana_selection_t *
-gmx_ana_selcollection_get_selection(gmx_ana_selcollection_t *sc, int i);
-/** Returns TRUE if the collection requires topology information for evaluation. */
-gmx_bool
-gmx_ana_selcollection_requires_top(gmx_ana_selcollection_t *sc);
-/** Prints a human-readable version of the internal selection element tree. */
-void
-gmx_ana_selcollection_print_tree(FILE *fp, gmx_ana_selcollection_t *sc, gmx_bool bValues);
-/** Prints the selection strings into an XVGR file as comments. */
-void
-xvgr_selcollection(FILE *fp, gmx_ana_selcollection_t *sc,
- const output_env_t oenv);
-
-/* In parsetree.c */
-/** Parses selection(s) from standard input. */
-int
-gmx_ana_selcollection_parse_stdin(gmx_ana_selcollection_t *sc, int nr,
- gmx_ana_indexgrps_t *grps,
- gmx_bool bInteractive);
-/** Parses selection(s) from a file. */
-int
-gmx_ana_selcollection_parse_file(gmx_ana_selcollection_t *sc, const char *fnm,
- gmx_ana_indexgrps_t *grps);
-/** Parses selection(s) from a string. */
-int
-gmx_ana_selcollection_parse_str(gmx_ana_selcollection_t *sc, const char *str,
- gmx_ana_indexgrps_t *grps);
-
-/* In compiler.c */
-/** Set debugging flag for selection compilation. */
-void
-gmx_ana_selcollection_set_compile_debug(gmx_ana_selcollection_t *sc, gmx_bool bDebug);
-/** Prepares the selections for evaluation and performs some optimizations. */
-int
-gmx_ana_selcollection_compile(gmx_ana_selcollection_t *sc);
-
-/* In evaluate.c */
-/** Evaluates the selection. */
-int
-gmx_ana_selcollection_evaluate(gmx_ana_selcollection_t *sc,
- t_trxframe *fr, t_pbc *pbc);
-/** Evaluates the largest possible index groups from dynamic selections. */
-int
-gmx_ana_selcollection_evaluate_fin(gmx_ana_selcollection_t *sc, int nframes);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \page selmethods Custom selection methods
- *
- * Custom selection methods are defined by creating a new instance of
- * \c gmx_ana_selmethod_t and filling it with the necessary data for handling
- * the selection.
- * The structure contains callback pointers that define the actual behavior
- * of the method.
- * The following sections discuss how the structure should be filled and how
- * to implement the callbacks.
- *
- *
- * \section selmethods_define \c gmx_ana_selmethod_t data structure
- *
- * An example \c gmx_ana_selmethod_t definition could look like this:
- *
- * \code
- * gmx_ana_selmethod_t sm_example = {
- * "example", GROUP_VALUE, 0,
- * asize(sm_params_example), sm_params_example,
- * &init_data_example,
- * NULL,
- * &init_example,
- * NULL,
- * &free_data_example,
- * &init_frame_example,
- * &evaluate_example,
- * NULL,
- * {"example from POS_EXPR [cutoff REAL]", 0, NULL},
- * };
- * \endcode
- *
- * The first value defines the name of the method.
- * It is used mostly for informational purposes; the actual name(s) recognized
- * by the selection parser are defined by the call to
- * gmx_ana_selmethod_register() (see \ref selmethods_register).
- *
- * The second value defines the type of the value the method returns.
- * Possible values are
- * - \ref NO_VALUE : This is allowed only for methods that have the flag
- * \ref SMETH_MODIFIER set (see \ref selmethods_modifiers).
- * - \ref INT_VALUE : The method returns one or more integer values.
- * - \ref REAL_VALUE : The method returns one or more floating-point values.
- * - \ref STR_VALUE : The method returns one or more strings.
- * - \ref POS_VALUE : The method returns one or more 3D vectors.
- * - \ref GROUP_VALUE : The method returns a single index group.
- *
- * The third value gives additional information about the method using
- * a combination of flags.
- * Possible flags are:
- * - \ref SMETH_REQTOP : If set, the topology information is always loaded
- * and the \p top pointer passed to the callbacks is guaranteed to be
- * non-NULL. Should be set if the method requires topology information
- * for evaluation.
- * - \ref SMETH_DYNAMIC : If set, the method can only be evaluated dynamically,
- * i.e., it requires data from the trajectory frame.
- * - \ref SMETH_MODIFIER : If set, the method is a selection modifier and
- * not an actual selection method.
- * For more details, see \ref selmethods_modifiers.
- *
- * There are two additional flags that specify the number of values the
- * method returns. Only one of them can be set at a time.
- * If neither is set, the default behavior is to evaluate a value for each
- * input atom (except for \ref GROUP_VALUE methods, which always return a
- * single group).
- * Other behaviors can be specified with these flags:
- * - \ref SMETH_SINGLEVAL : If set, the method evaluates to a single value.
- * This is automatically set if the type is \ref GROUP_VALUE.
- * - \ref SMETH_VARNUMVAL : If set, the method evaluates to an arbitrary
- * number of values.
- * The number of values is determined based on the values given by the user
- * to the method parameters (see \ref selmethods_params).
- * .
- * If either of these flags is specified (and the method type is not
- * \ref GROUP_VALUE), the group passed to the evaluation callback should not
- * be used as it can be NULL.
- * Currently, the above flags only work (have been tested) for \ref POS_VALUE
- * methods.
- *
- * There is one additional flag that can only be specified for \ref STR_VALUE
- * methods: \ref SMETH_CHARVAL . It is meant for to ease implementation of
- * methods that evaluate to strings consisting of single characters.
- *
- * The next two values determine the number of parameters and a pointer to
- * the parameter array. The contents of the parameter array are described in
- * \ref selmethods_params. If the method does not take parameters, the first
- * value should be 0 and the second can be NULL.
- * Currently, \ref STR_VALUE methods cannot take parameters, but this limitation
- * should be easy to lift if required.
- *
- * These are followed by function callbacks that determine the
- * actual behavior of the method. Any of these except the evaluation callback
- * can be NULL (the evaluation callback can also be NULL if \ref NO_VALUE is
- * specified for a selection modifier). However, the presence of parameters
- * can require some of the callbacks to be implemented.
- * The details are described in \ref selmethods_callbacks.
- *
- * Finally, there is a data structure that gives help texts for the method.
- *
- * The \c gmx_ana_selmethod_t variable should be declared as a global variable
- * or it should be otherwise ensured that the structure is not freed: only a
- * pointer to the structure is stored by the library.
- *
- *
- * \section selmethods_params Defining parameters
- *
- * Parameters to selection methods are defined in a separate array of
- * \c gmx_ana_selparam_t structures.
- * The order of the parameters does not matter (except possibly for callback
- * implementation), with one important exception:
- * If the method evaluates to a \ref POS_VALUE, the first parameter should
- * have \ref GROUP_VALUE and be the one that is used to calculate the
- * positions.
- *
- * An example parameter definition:
- * \code
- * static gmx_ana_selparam_t sm_params_example[] = {
- * {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
- * {"from", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
- * };
- * \endcode
- *
- * The first value gives the name of the parameter.
- * The first parameter can have a NULL name, which means that the value should
- * immediately follow the method name. This can be used to specify methods
- * of the type 'within 5 of ...'.
- *
- * The second value specifies the type of the value that the parameter accepts.
- * \ref NO_VALUE can be used to specify a boolean parameter, other possibilities
- * are the same as for the selection method type.
- *
- * The third value gives the number of values that the parameter accepts.
- * For boolean parameters (\ref NO_VALUE), it should be 0.
- * For parameters with \ref SPAR_VARNUM of \ref SPAR_ATOMVAL, it should be set
- * to -1 for consistency (it is not used).
- * If \ref SPAR_RANGES is specified, it should be either 1 (to accept a single
- * continuous range) or -1 (if combined with \ref SPAR_VARNUM).
- * In all other cases, it should be a positive integer; in most cases, it
- * should be 1.
- *
- * The nest two pointers should always be NULL (they should be initialized in
- * the callbacks), except the first pointer in the case of \ref SPAR_ENUMVAL
- * (see below).
- *
- * The final value gives additional information about the acceptable values
- * for the parameter using a combination of flags.
- * The possible flags are:
- * - \ref SPAR_OPTIONAL : If set, the user does not need to provide a value
- * for the parameter. If not set, an error is reported if the parameter
- * is not specified by the user.
- * - \ref SPAR_DYNAMIC : If set, the method can handle dynamic values for
- * the parameter, i.e., the value(s) can be given by an expression that
- * evaluates to different values for different frames.
- * - \ref SPAR_RANGES : Can be set only for \ref INT_VALUE and
- * \ref REAL_VALUE parameters,
- * and cannot be combined with \ref SPAR_DYNAMIC.
- * If set, the parameter accepts ranges of values.
- * The ranges are automatically sorted and compacted such that a minimum
- * amount of non-overlapping ranges are given for the method.
- * - \ref SPAR_VARNUM : If set, the parameter can have a variable number
- * of values. These can be provided by the user as a list of values, or
- * using a single \ref SMETH_VARNUMVAL (or a single \ref SMETH_SINGLEVAL)
- * method.
- * - \ref SPAR_ATOMVAL : If set, the parameter accepts either a single value
- * or an expression that evaluates to a value for each input atom.
- * The single input value is treated as if the same value was returned for
- * each atom.
- * Cannot be combined with \ref SPAR_RANGES or \ref SPAR_VARNUM.
- * - \ref SPAR_ENUMVAL : Can only be set for \ref STR_VALUE parameters that
- * take a single value, and cannot be combined with any other flag than
- * \ref SPAR_OPTIONAL. If set, the parameter only accepts one of predefined
- * string values. See \ref SPAR_ENUMVAL documentation for details on how
- * to specify the acceptable values.
- *
- *
- * \section selmethods_callbacks Implementing callbacks
- *
- * There are eight differen callback functions that can be implemented for
- * selection methods: sel_datafunc(), sel_posfunc(), sel_initfunc(),
- * sel_outinitfunc(), sel_freefunc(), sel_framefunc(), and two update functions.
- * They are in this order in the \c gmx_ana_selmethod_t data structure.
- * In general, any of the callbacks can be NULL, but the presence of
- * parameters or other callbacks imposes some restrictions:
- * - sel_datafunc() should be provided if the method takes parameters.
- * - sel_initfunc() should be provided if the method takes
- * any parameters with the \ref SPAR_VARNUM or \ref SPAR_ATOMVAL flags,
- * except if those parameters have a \ref POS_VALUE.
- * - sel_outinitfunc() should be provided for \ref POS_VALUE methods
- * and \ref SMETH_VARNUMVAL methods.
- * - sel_freefunc() should be provided if sel_datafunc() and/or
- * sel_initfunc() allocate any dynamic memory in addition to the data
- * structure itself.
- * - sel_updatefunc_pos() only makes sense for methods with \ref SMETH_DYNAMIC
- * set.
- * - At least one update function should be provided unless the method type is
- * \ref NO_VALUE.
- *
- * The documentations for the function pointer types provide more information
- * about how the callbacks should be implemented.
- *
- *
- * \section selmethods_modifiers Selection modifiers
- *
- * Selection modifiers are a special kind of selection methods that can be
- * appended to the end of a selection. They are specified by adding the
- * \ref SMETH_MODIFIER flag to the \c gmx_ana_selmethod_t.
- * They can have two different types:
- * - \ref POS_VALUE : These modifiers are given the final positions
- * as an input, and they can make modifications to the selection that are
- * not possible otherwise (e.g., permute the atoms).
- * The modifier should implement sel_updatefunc_pos() and also have
- * one NULL parameter in the beginning of the parameter list that takes
- * \ref POS_VALUE and is used to give the input positions.
- * - \ref NO_VALUE : These modifiers do not modify the final selection, but
- * can be used to implement per-selection options for analysis tools
- * or to control the default behavior of the selection engine
- * (currently, such a framework is not implemented, but should be easy to
- * implement if required).
- *
- * In addition to restricting the type of the method, selection modifiers
- * do not allow the flags \ref SMETH_SINGLEVAL and \ref SMETH_VARNUMVAL
- * (they would not make sense).
- *
- * Parameters and callbacks should be implemented as with normal selection
- * method, but beware that very little of the functionality has been tested.
- *
- * \todo
- * The modifier handling could be made more flexible and more generic;
- * the current implementation does not allow many things which would be
- * possible with slight changes in the internals of the library.
- *
- *
- * \section selmethods_register Registering the method
- *
- * After defining the method with \c gmx_ana_selmethod_t, it should be
- * registered with the selection engine.
- * In analysis programs, this can be done by calling
- * gmx_ana_selmethod_register().
- * If adding the method to the library, you should add a pointer to the new
- * method structure into the \c smtable_def array (in \ref selmethod.c
- * "selmethod.c"), and it is registered automatically.
- * In both cases, gmx_ana_selmethod_register() does several checks on the
- * structure and reports any errors or inconsistencies it finds.
- */
-/*! \file
- * \brief API for handling selection methods.
- *
- * There should be no need to use the data structures or call the
- * functions in this file directly unless implementing a custom selection
- * method.
- *
- * Instructions for implementing custom selection methods can be found
- * on a separate page: \ref selmethods
- */
-#ifndef SELMETHOD_H
-#define SELMETHOD_H
-
-#include "typedefs.h"
-
-#include "indexutil.h"
-#include "selparam.h"
-#include "selvalue.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-struct gmx_ana_pos_t;
-struct gmx_ana_poscalc_coll_t;
-struct gmx_ana_selcollection_t;
-
-/*! \name Selection method flags
- * \anchor selmethod_flags
- */
-/*@{*/
-/*! \brief
- * If set, the method requires topology information.
- */
-#define SMETH_REQTOP 1
-/*! \brief
- * If set, the method can only be evaluated dynamically.
- */
-#define SMETH_DYNAMIC 2
-/*! \brief
- * If set, the method evaluates to a single value.
- *
- * The default is that the method evaluates to a value for each input atom.
- * Cannot be combined with \ref SMETH_VARNUMVAL.
- */
-#define SMETH_SINGLEVAL 4
-/*! \brief
- * If set, the method evaluates to an arbitrary number of values.
- *
- * The default is that the method evaluates to a value for each input atom.
- * Cannot be combined with \ref SMETH_SINGLEVAL or with \ref GROUP_VALUE.
- */
-#define SMETH_VARNUMVAL 8
-/*! \brief
- * If set, the method evaluates to single-character strings.
- *
- * This flag can only be set for \ref STR_VALUE methods. If it is set, the
- * selection engine automatically allocates and frees the required strings.
- * The evaluation function should store the character values as the first
- * character in the strings in the output data structure and should not change
- * the string pointers.
- */
-#define SMETH_CHARVAL 64
-/*! \brief
- * If set, the method is a selection modifier.
- *
- * The method type should be \ref GROUP_VALUE or \ref NO_VALUE .
- * Cannot be combined with \ref SMETH_SINGLEVAL or \ref SMETH_VARNUMVAL .
- */
-#define SMETH_MODIFIER 256
-/*@}*/
-
-/*! \brief
- * Allocates and initializes internal data and parameter values.
- *
- * \param[in] npar Number of parameters in \p param.
- * \param[in,out] param Pointer to (a copy of) the method's
- * \c gmx_ana_selmethod_t::param.
- * \returns Pointer to method-specific data structure.
- * This pointer will be passed as the last parameter of all other function
- * calls.
- * Should return NULL on error (only error that should occur is out of
- * memory).
- *
- * Should allocate and initialize any internal data required by the method.
- * Should also initialize the value pointers (\c gmx_ana_selparam_t::val) in
- * \p param to point to variables within the internal data structure,
- * with the exception of parameters that specify the \ref SPAR_VARNUM or
- * the \ref SPAR_ATOMVAL flag (these should be handled in sel_initfunc()).
- * However, parameters with a position value should be initialized.
- * It is also possible to initialize \ref SPAR_ENUMVAL statically outside
- * this function (see \ref SPAR_ENUMVAL).
- * The \c gmx_ana_selparam_t::nvalptr should also be initialized for
- * non-position-valued parameters that have both \ref SPAR_VARNUM and
- * \ref SPAR_DYNAMIC set (it can also be initialized for other parameters if
- * desired, but the same information will be available through other means).
- * For optional parameters, the default values can (and should) be initialized
- * here, as the parameter values are not changed if the parameter is not
- * provided.
- *
- * For boolean parameters (type equals \ref NO_VALUE), the default value
- * should be set here. The user can override the value by giving the parameter
- * either as 'NAME'/'noNAME', or as 'NAME on/off/yes/no'.
- *
- * If the method takes any parameters, this function must be provided.
- */
-typedef void *(*sel_datafunc)(int npar, gmx_ana_selparam_t *param);
-/*! \brief
- * Sets the position calculation collection for the method.
- *
- * \param[in] pcc Position calculation collection that the method should use
- * for position calculations.
- * \param data Internal data structure from sel_datafunc().
- *
- * This function should be provided if the method uses the routines from
- * poscalc.h for calculating positions.
- * The pointer \p pcc should then be stored and used for initialization for
- * any position calculation structures.
- */
-typedef void (*sel_posfunc)(struct gmx_ana_poscalc_coll_t *pcc, void *data);
-/*! \brief
- * Does initialization based on topology and/or parameter values.
- *
- * \param[in] top Topology structure
- * (can be NULL if \ref SMETH_REQTOP is not set).
- * \param[in] npar Number of parameters in \p param.
- * \param[in] param Pointer to (an initialized copy of) the method's
- * \c gmx_ana_selmethod_t::param.
- * \param data Internal data structure from sel_datafunc().
- * \returns 0 on success, a non-zero error code on failure.
- *
- * This function is called after the parameters have been processed:
- * the values of the parameters are stored at the locations set in
- * sel_datafunc().
- * The flags \ref SPAR_DYNAMIC and \ref SPAR_ATOMVAL are cleared before
- * calling the function if the value is static or single-valued, respectively.
- * If a parameter had the \ref SPAR_VARNUM or \ref SPAR_ATOMVAL flag (and
- * is not \ref POS_VALUE), a pointer to the memory allocated for the values is
- * found in \c gmx_ana_selparam_t::val.
- * The pointer should be stored by this function, otherwise the values
- * cannot be accessed.
- * For \ref SPAR_VARNUM parameters, the number of values can be accessed
- * through \c gmx_ana_selparam_t::val. For parameters with \ref SPAR_DYNAMIC,
- * the number is the maximum number of values (the actual number can be
- * accessed in sel_framefunc() and in the update callback through the value
- * pointed by \c gmx_ana_selparam_t::nvalptr).
- * For \ref SPAR_ATOMVAL parameters, \c gmx_ana_selparam_t::val::nr is set to
- * 1 if a single value was provided, otherwise it is set to the maximum number
- * of values possibly passed to the method.
- * The value pointed by \c gmx_ana_selparam_t::nvalptr always contains the same
- * value as \c gmx_ana_selparam_t::val::nr.
- *
- * For dynamic \ref GROUP_VALUE parameters (\ref SPAR_DYNAMIC set), the value
- * will be the largest possible selection that may occur during the
- * evaluation. For other types of dynamic parameters, the values are
- * undefined.
- *
- * If the method takes any parameters with the \ref SPAR_VARNUM or
- * \ref SPAR_ATOMVAL flags, this function must be provided, except if these
- * parameters all have \ref POS_VALUE.
- *
- * This function may be called multiple times for the same method if the
- * method takes parameters with \ref SPAR_ATOMVAL set.
- */
-typedef int (*sel_initfunc)(t_topology *top, int npar,
- gmx_ana_selparam_t *param, void *data);
-/*! \brief
- * Initializes output data structure.
- *
- * \param[in] top Topology structure
- * (can be NULL if \ref SMETH_REQTOP is not set).
- * \param[in,out] out Output data structure.
- * \param[in] data Internal data structure from sel_datafunc().
- * \returns 0 on success, an error code on error.
- *
- * This function is called immediately after sel_initfunc().
- *
- * If the method evaluates to a position (\ref POS_VALUE), this function
- * should be provided, and it should initialize the \c gmx_ana_pos_t data
- * structure pointed by \p out.p (the pointer is guaranteed to be non-NULL).
- * The \p out.p->g pointer should be initialized to the group that is used
- * to evaluate positions in sel_updatefunc() or sel_updatefunc_pos().
- *
- * The function should also be provided for non-position-valued
- * \ref SMETH_VARNUMVAL methods. For these methods, it suffices to set the
- * \p out->nr field to reflect the maximum number of values returned by the
- * method.
- *
- * Currently, this function is not needed for other types of methods.
- *
- * This function may be called multiple times for the same method if the
- * method takes parameters with \ref SPAR_ATOMVAL set.
- */
-typedef int (*sel_outinitfunc)(t_topology *top, gmx_ana_selvalue_t *out,
- void *data);
-/*! \brief
- * Frees the internal data.
- *
- * \param[in] data Internal data structure from sel_datafunc().
- *
- * This function should be provided if the internal data structure contains
- * dynamically allocated data, and should free any such data.
- * The data structure itself should not be freed; this is handled automatically.
- * If there is no dynamically allocated data within the structure,
- * this function is not needed.
- * Any memory pointers received as values of parameters are managed externally,
- * and should not be freed.
- * Pointers set as the value pointer of \ref SPAR_ENUMVAL parameters should not
- * be freed.
- */
-typedef void (*sel_freefunc)(void *data);
-
-/*! \brief
- * Initializes the evaluation for a new frame.
- *
- * \param[in] top Topology structure
- * (can be NULL if \ref SMETH_REQTOP is not set).
- * \param[in] fr Current frame.
- * \param[in] pbc Initialized periodic boundary condition structure,
- * or NULL if PBC should not be used.
- * \param data Internal data structure from sel_datafunc().
- * \returns 0 on success, a non-zero error code on failure.
- *
- * This function should be implemented if the selection method needs to
- * do some preprocessing for each frame, and the preprocessing does not
- * depend on the evaluation group.
- * Because \p sel_updatefunc_* can be called more than once for a frame,
- * it is inefficient do the preprocessing there.
- * It is ensured that this function will be called before
- * \p sel_updatefunc_* for each frame, and that it will be called at most
- * once for each frame.
- * For static methods, it is called once, with \p fr and \p pbc set to
- * NULL.
- */
-typedef int (*sel_framefunc)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- void *data);
-/*! \brief
- * Evaluates a selection method.
- *
- * \param[in] top Topology structure
- * (can be NULL if \ref SMETH_REQTOP is not set).
- * \param[in] fr Current frame.
- * \param[in] pbc Initialized periodic boundary condition structure,
- * or NULL if PBC should not be used.
- * \param[in] g Index group for which the method should be evaluated.
- * \param[out] out Output data structure.
- * \param data Internal data structure from sel_datafunc().
- * \returns 0 on success, a non-zero error code on error.
- *
- * This function should evaluate the method for each atom included in \p g,
- * and write the output to \p out. The pointer in the union \p out->u that
- * corresponds to the type of the method should be used.
- * Enough memory has been allocated to store the output values.
- * The number of values in \p out should also be updated if necessary.
- * However, \ref POS_VALUE or \ref GROUP_VALUE methods should not touch
- * \p out->nr (it should be 1 anyways).
- *
- * For \ref STR_VALUE methods, the pointers stored in \p out->s are discarded
- * without freeing; it is the responsibility of this function to provide
- * pointers that can be discarded without memory leaks.
- */
-typedef int (*sel_updatefunc)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out,
- void *data);
-/*! \brief
- * Evaluates a selection method using positions.
- *
- * \param[in] top Topology structure
- * (can be NULL if \ref SMETH_REQTOP is not set).
- * \param[in] fr Current frame.
- * \param[in] pbc Initialized periodic boundary condition structure,
- * or NULL if PBC should not be used.
- * \param[in] pos Positions for which the method should be evaluated.
- * \param[out] out Output data structure.
- * \param data Internal data structure from sel_datafunc().
- * \returns 0 on success, a non-zero error code on error.
- *
- * This function should evaluate the method for each position in \p g,
- * and write the output values to \p out. The pointer in the union \p out->u
- * that corresponds to the type of the method should be used.
- * Enough memory has been allocated to store the output values.
- * The number of values in \p out should also be updated if necessary.
- * However, \ref POS_VALUE or \ref GROUP_VALUE methods should not touch
- * \p out->nr (it should be 1 anyways).
- *
- * For \ref STR_VALUE methods, the pointers stored in \p out->s are discarded
- * without freeing; it is the responsibility of this function to provide
- * pointers that can be discarded without memory leaks.
- */
-typedef int (*sel_updatefunc_pos)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- struct gmx_ana_pos_t *pos,
- gmx_ana_selvalue_t *out,
- void *data);
-
-/*! \brief
- * Help information for a selection method.
- *
- * If some information is not available, the corresponding field can be set to
- * 0/NULL.
- */
-typedef struct gmx_ana_selmethod_help_t
-{
- /*! \brief
- * One-line description of the syntax of the method.
- *
- * If NULL, the name of the method is used.
- */
- const char *syntax;
- /*! \brief
- * Number of strings in \p help.
- *
- * Set to 0 if \p help is NULL.
- */
- int nlhelp;
- /*! \brief
- * Detailed help for the method.
- *
- * If there is no help available in addition to \p syntax, this can be set
- * to NULL.
- */
- const char **help;
-} gmx_ana_selmethod_help_t;
-
-/*! \brief
- * Describes a selection method.
- *
- * Any of the function pointers except the update call can be NULL if the
- * operation is not required or not supported. In this case,
- * corresponding function calls are skipped.
- *
- * See the function pointer type documentation for details of how the
- * functions should be implemented.
- * More details on implementing new selection methods can be found on a
- * separate page: \ref selmethods.
- */
-typedef struct gmx_ana_selmethod_t
-{
- /** Name of the method. */
- const char *name;
- /** Type which the method returns. */
- e_selvalue_t type;
- /*! \brief
- * Flags to specify how the method should be handled.
- *
- * See \ref selmethod_flags for allowed values.
- */
- int flags;
- /** Number of parameters the method takes. */
- int nparams;
- /** Pointer to the array of parameter descriptions. */
- gmx_ana_selparam_t *param;
-
- /** Function for allocating and initializing internal data and parameters. */
- sel_datafunc init_data;
- /** Function to set the position calculation collection. */
- sel_posfunc set_poscoll;
- /** Function to do initialization based on topology and/or parameter values. */
- sel_initfunc init;
- /** Function to initialize output data structure. */
- sel_outinitfunc outinit;
- /** Function to free the internal data. */
- sel_freefunc free;
-
- /** Function to initialize the calculation for a new frame. */
- sel_framefunc init_frame;
- /** Function to evaluate the value. */
- sel_updatefunc update;
- /** Function to evaluate the value using positions. */
- sel_updatefunc_pos pupdate;
-
- /** Help data for the method. */
- gmx_ana_selmethod_help_t help;
-} gmx_ana_selmethod_t;
-
-/** Registers a selection method. */
-int
-gmx_ana_selmethod_register(struct gmx_ana_selcollection_t *sc,
- const char *name, gmx_ana_selmethod_t *method);
-/** Registers all selection methods in the library. */
-int
-gmx_ana_selmethod_register_defaults(struct gmx_ana_selcollection_t *sc);
-
-/** Finds a parameter from a selection method by name. */
-gmx_ana_selparam_t *
-gmx_ana_selmethod_find_param(const char *name, gmx_ana_selmethod_t *method);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief API for handling parameters used in selections.
- *
- * There should be no need to use the data structures or call the
- * functions in this file directly unless implementing a custom selection
- * method.
- *
- * More details can be found on the page discussing
- * \ref selmethods "custom selection methods".
- */
-#ifndef SELPARAM_H
-#define SELPARAM_H
-
-#include "typedefs.h"
-
-#include "indexutil.h"
-#include "selvalue.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/*! \name Parameter flags
- * \anchor selparam_flags
- */
-/*@{*/
-/*! \brief
- * This flag is set if the user has provided the parameter.
- *
- * This flag is set automatically, and should not be set by the user.
- */
-#define SPAR_SET 1
-/*! \brief
- * If not set, an error is reported if the parameter is not specified by the
- * user.
- */
-#define SPAR_OPTIONAL 2
-/*! \brief
- * If set, the parameter value can be dynamic, i.e., be different for
- * different frames.
- *
- * If set, the parameter value should only be accessed in the update function
- * of \c gmx_ana_selmethod_t.
- * The flag is cleared before sel_initfunc() if the value provided is actually
- * static.
- */
-#define SPAR_DYNAMIC 4
-/*! \brief
- * If set, the parameter value is parsed into sorted ranges.
- *
- * Can only be specified for integer parameters.
- * If specified, the value of the parameter (\c gmx_ana_selparam_t::val)
- * consists of sets of two integers, each specifying a range.
- * The values give the endpoints of the ranges (inclusive).
- * The ranges are sorted and overlapping/continuous ranges are merged into
- * a single range to minimize the number of ranges.
- *
- * If this flag is specified, \c gmx_ana_selparam_t::nval gives the number of
- * ranges. \p gmx_ana_selparam_t::nval should be 1 or \ref SPAR_VARNUM should be
- * specified; other values would lead to unpredictable behavior.
- */
-#define SPAR_RANGES 8
-/*! \brief
- * If set, the parameter can have any number of values.
- *
- * If specified, the data pointer in \c gmx_ana_selparam_t::val should be NULL;
- * the memory is allocated by the parameter parser.
- * The implementation of the method should ensure that the pointer to the
- * allocated memory is stored somewhere in sel_initfunc();
- * otherwise, the memory is lost.
- *
- * The initial value of \c gmx_ana_selparam_t::nval is not used with this flag.
- * Instead, it will give the number of actual values provided by the user
- * after the parameters have been parsed.
- * For consistency, it should be initialized to -1.
- *
- * Cannot be combined with \ref GROUP_VALUE parameters.
- */
-#define SPAR_VARNUM 16
-/*! \brief
- * If set, the parameter can have a separate value for each atom.
- *
- * The flag is cleared before sel_initfunc() if the value provided is actually
- * a single value.
- *
- * Cannot be combined with \ref POS_VALUE or \ref GROUP_VALUE parameters.
- */
-#define SPAR_ATOMVAL 32
-/*! \brief
- * If set, the parameter takes one of a set of predefined strings.
- *
- * Can only be specified for a \ref STR_VALUE parameter that takes a single
- * string.
- * The data pointer in \c gmx_ana_selparam_t::val should be initialized into an
- * array of strings such that the first and last elements are NULL, and the
- * rest give the possible values. For optional values, the second element in
- * the array should give the default value. The string given by the user is
- * matched against the beginnings of the given strings, and if a unique match
- * is found, the first pointer in the array will be initialized to point to
- * the matching string.
- * The data pointer can be initialized as a static array; duplication of the
- * array for multiple instances of the same method is automatically taken care
- * of.
- */
-#define SPAR_ENUMVAL 128
-/*@}*/
-
-/*! \brief
- * Describes a single parameter for a selection method.
- */
-typedef struct gmx_ana_selparam_t
-{
- /** Name of the parameter. */
- const char *name;
- /*! \brief
- * The parameter value.
- *
- * Type \ref NO_VALUE can be used to define a boolean parameter.
- * The number of values should be 0 for boolean parameters.
- *
- * The value pointer be initialized to NULL in the definition of a
- * \c gmx_ana_selmethod_t and initialized in the
- * \c gmx_ana_selmethod_t::init_data call
- * (see sel_datafunc()).
- * However, if \ref SPAR_VARNUM is provided and the parameter is not
- * \ref POS_VALUE, this field should not be initialized. Instead,
- * sufficient memory is allocated automatically and the pointer should be
- * stored in \c gmx_ana_selmethod_t::init
- * (see sel_initfunc()).
- *
- * The values cannot be accessed outside these two functions: the compiler
- * makes a copy of the parameter structure for each instance of the
- * method, and the original parameter array is not changed.
- */
- gmx_ana_selvalue_t val;
- /*! \brief
- * Pointer to store the number of values.
- *
- * If not NULL, the number of values for the parameter is stored in the
- * pointed value.
- * Should be specified if \ref SPAR_VARNUM and \ref SPAR_DYNAMIC are both
- * set.
- *
- * Should be initialized to NULL in the definition a \c gmx_ana_selmethod_t
- * and initialized in sel_datafunc().
- */
- int *nvalptr;
- /*! \brief
- * Flags that alter the way the parameter is parsed/handled.
- *
- * See \ref selparam_flags for allowed values.
- */
- int flags;
-} gmx_ana_selparam_t;
-
-/** Finds a parameter from an array by name. */
-gmx_ana_selparam_t *
-gmx_ana_selparam_find(const char *name, int nparam, gmx_ana_selparam_t *param);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief Declaration of \c gmx_ana_selvalue_t.
- *
- * There should be no need to use the data structures in this file directly
- * unless implementing a custom selection routine.
- */
-#ifndef SELVALUE_H
-#define SELVALUE_H
-
-#include "types/simple.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/** Defines the value type of a different selection objects. */
-typedef enum
-{
- NO_VALUE, /**< No value; either an error condition or an gmx_boolean
- parameter. */
- INT_VALUE, /**< One or more integer values. */
- REAL_VALUE, /**< One or more real values. */
- STR_VALUE, /**< One or more string values. */
- POS_VALUE, /**< One or more position values. */
- GROUP_VALUE /**< One group of atoms. */
-} e_selvalue_t;
-
-/*! \brief
- * Describes a value of a selection expression or of a selection method
- * parameter.
- *
- * Which field in the union is used depends on the \p type.
- */
-typedef struct gmx_ana_selvalue_t
-{
- /** Type of the value. */
- e_selvalue_t type;
- /*! \brief
- * Number of values in the array pointed by the union.
- *
- * Note that for position and group values, it is the number of
- * data structures in the array, not the number of positions or
- * the number of atoms in the group.
- */
- int nr;
- /** Pointer to the value. */
- union {
- /*! \brief
- * Generic pointer for operations that do not need type information.
- *
- * Needs to be the first member to be able to use initialized arrays.
- */
- void *ptr;
- /** Integer value(s) (type \ref INT_VALUE). */
- int *i;
- /** Real value(s) (type \ref REAL_VALUE). */
- real *r;
- /** String value(s) (type \ref STR_VALUE). */
- char **s;
- /** Structure for the position value(s) (type \ref POS_VALUE). */
- struct gmx_ana_pos_t *p;
- /** Group value (type \ref GROUP_VALUE). */
- struct gmx_ana_index_t *g;
- /** Boolean value (only parameters of type \ref NO_VALUE); */
- gmx_bool *b;
- } u;
- /*! \brief
- * Number of elements allocated for the value array.
- */
- int nalloc;
-} gmx_ana_selvalue_t;
-
-/** Initializes an empty selection value structure. */
-void
-_gmx_selvalue_clear(gmx_ana_selvalue_t *val);
-/** Reserve memory for storing selection values. */
-int
-_gmx_selvalue_reserve(gmx_ana_selvalue_t *val, int n);
-/** Sets the memory for storing selection values. */
-int
-_gmx_selvalue_setstore(gmx_ana_selvalue_t *val, void *ptr);
-/** Sets the memory for storing selection values and marks it for automatic freeing. */
-int
-_gmx_selvalue_setstore_alloc(gmx_ana_selvalue_t *val, void *ptr, int nalloc);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gromacs Runs On Most of All Computer Systems
- */
-/*! \file
- * \brief Generic string handling functions.
- */
-#ifndef _string2_h
-#define _string2_h
-
-/*
- *
- * string2.h
- * David van der Spoel
- *
- */
-
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <time.h>
-#include <errno.h>
-
-/*#include "typedefs.h"*/
-#include "types/simple.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define CONTINUE '\\'
-#define COMMENTSIGN ';'
-
-int continuing(char *s);
-
-char *fgets2(char *s, int n, FILE *stream);
-
-void strip_comment (char *line);
-
-int break_line (char *line,
- char *variable,
- char *value);
-
-void upstring (char *str);
-
-void ltrim (char *str);
-
-void rtrim (char *str);
-
-void trim (char *str);
-
-void nice_header (FILE *out,const char *fn);
-
-int gmx_strcasecmp_min(const char *str1, const char *str2);
-int gmx_strncasecmp_min(const char *str1, const char *str2, int n);
-/* This funny version of strcasecmp, is not only case-insensitive,
- * but also ignores '-' and '_'.
- */
-
-int gmx_strcasecmp(const char *str1, const char *str2);
-int gmx_strncasecmp(const char *str1, const char *str2, int n);
-
-char *gmx_strdup(const char *src);
-char *gmx_strndup(const char *src, int n);
-
-/** Pattern matcing with wildcards. */
-int gmx_wcmatch(const char *pattern, const char *src);
-
-/** Return value for gmx_wcmatch() when there is no match. */
-#define GMX_NO_WCMATCH 1
-
-
-/* this is our implementation of strsep, the thread-safe replacement for
- strtok */
-char *gmx_strsep(char **stringp, const char *delim);
-
-
-char *wrap_lines(const char *buf,int line_width, int indent,
- gmx_bool bIndentFirst);
-/* wraps lines at 'linewidth', indenting all following
- * lines by 'indent' spaces. A temp buffer is allocated and returned,
- * which can be disposed of if no longer needed.
- * If !bIndentFirst, then the first line will not be indented, only
- * the lines that are created due to wapping.
- */
-
-
-char **split(char sep,char *str);
-/* Implementation of the well-known Perl function split */
-
-gmx_large_int_t str_to_large_int_t(const char *str, char **endptr);
-
-#if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
-#define snprintf _snprintf
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _string2_h */
+++ /dev/null
-# includes: Nothing to build, just installation
-install(DIRECTORY . DESTINATION include/thread_mpi
-
- PATTERN "Makefile*" EXCLUDE
- PATTERN "CMake*" EXCLUDE
- PATTERN "cmake*" EXCLUDE
- PATTERN "*~" EXCLUDE
-)
-
-
+++ /dev/null
-# Process this file with automake to produce Makefile.in
-#
-# Don't edit - this file is generated automatically from Makefile.am
-#
-
-SUBDIRS = atomic
-
-pkgincludethread_mpidir = ${pkgincludedir}/thread_mpi
-
-pkgincludethread_mpi_HEADERS = \
- atomic.h event.h lock.h tmpi.h \
- barrier.h hwinfo.h mpi_bindings.h wait.h \
- collective.h list.h threads.h
-
-
+++ /dev/null
-# includes: Nothing to build, just installation
-install(DIRECTORY . DESTINATION include/thread_mpi/atomic
-
- PATTERN "Makefile*" EXCLUDE
- PATTERN "CMake*" EXCLUDE
- PATTERN "cmake*" EXCLUDE
- PATTERN "*~" EXCLUDE
-)
-
-
+++ /dev/null
-# Process this file with automake to produce Makefile.in
-#
-# Don't edit - this file is generated automatically from Makefile.am
-#
-
-pkgincludethread_mpidir = ${pkgincludedir}/thread_mpi/atomic
-
-pkgincludethread_mpi_HEADERS = \
- cycles.h gcc_intrinsics.h gcc_x86.h\
- gcc.h gcc_ppc.h msvc.h\
- gcc_ia64.h gcc_spinlock.h xlc_ppc.h
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \file
- * \brief Main API of the trajectory analysis library.
- *
- * Contains the API for the core analysis library.
- *
- * \todo
- * Better handling of reference groups.
- * It would be nice to be able to provide a string that would be used in
- * prompting the groups, and also in automatic reporting of what the tool
- * is about to do.
- *
- * Most analysis tools should include trajana.h
- * (which automatically includes indexutil.h, selection.h, position.h)
- * and possibly one or more of the following headers:
- * displacement.h, histogram.h, nbsearch.h.
- * If the tool implements custom selection methods, it should also include
- * selmethod.h (which automatically includes selparam.h and selvalue.h).
- *
- * Other headers (centerofmass.h, poscalc.h) are used internally by the
- * library to calculate positions.
- * Analysis tools should preferably not use the routines in these headers
- * directly, but instead get all positions through selections. This makes
- * them more flexible with a minimal amount of work.
- */
-#ifndef TRAJANA_H
-#define TRAJANA_H
-
-#include "typedefs.h"
-#include "filenm.h"
-#include "readinp.h"
-
-#include "indexutil.h"
-#include "selection.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Data structure for trajectory analysis tools. */
-typedef struct gmx_ana_traj_t gmx_ana_traj_t;
-
-/*! \name Flags for gmx_ana_traj_create()
- * \anchor analysis_flags
- * These flags can be used to alter the behavior of the analysis library to
- * suit the analysis tool.
- * They are given to the gmx_ana_traj_create() when creating the
- * \c gmx_ana_traj_t data structure, and affect the behavior of the other
- * functions in this header.
- */
-/*@{*/
-/*! \brief
- * Force loading of a topology file.
- *
- * If this flag is not specified, the topology file is loaded only if it is
- * provided on the command line explicitly.
- *
- * \see gmx_ana_get_topology()
- */
-#define ANA_REQUIRE_TOP (1<<0)
-/*! \brief
- * Do no free the coordinates loaded from the topology.
- *
- * If this flag is specified, the coordinates loaded from the topology can
- * be accessed with gmx_ana_get_topconf().
- *
- * \see gmx_ana_get_topconf()
- */
-#define ANA_USE_TOPX (1<<1)
-/*! \brief
- * Disallows the user from changing PBC handling.
- *
- * If this option is not specified, the analysis program (see gmx_analysisfunc())
- * may be passed a NULL PBC structure, and it should be able to handle such a
- * situation.
- */
-#define ANA_NOUSER_PBC (1<<4)
-/*! \brief
- * Disallows the user from changing PBC removal.
- */
-#define ANA_NOUSER_RMPBC (1<<5)
-/*! \brief
- * Disallows dynamic selections.
- *
- * If this flag is specified, an error is reported if the user specifies
- * any dynamic selections.
- */
-#define ANA_NO_DYNSEL (1<<8)
-/*! \brief
- * Disallows breaking of residues in dynamic selections.
- *
- * Makes it impossible for the user to select atom-based dynamic selections.
- *
- * Only has effect if \ref ANA_NO_DYNSEL is not specified.
- */
-#define ANA_REQUIRE_WHOLE (1<<9)
-/*! \brief
- * Disables automatic initialization of selection groups.
- *
- * If this flag is specified, parse_trjana_args() does not call
- * gmx_ana_init_selections(), allowing the program to do some initialization
- * before the call.
- * In particular, the program can use gmx_ana_set_nrefgprs() and
- * gmx_ana_set_nanagrps() before calling gmx_ana_init_selections() to
- * control the number of selections to expect.
- * This is useful if the program requires a different number of index groups
- * with different command-line arguments.
- * If the flag is specified, the program should call gmx_ana_init_selections()
- * exactly once after the parse_trjana_args() call.
- */
-#define ANA_USER_SELINIT (1<<10)
-/*! \brief
- * Allow only atomic positions to be selected.
- *
- * Note that this flag only applies to the analysis groups, not the reference
- * groups. The reference groups should be checked in the analysis program
- * if some limitations are imposed on them.
- */
-#define ANA_ONLY_ATOMPOS (1<<11)
-/*! \brief
- * Use masks in the positions to convey dynamic information.
- *
- * If this flag is specified, the positions calculated for the selections
- * are calculated always for the same group of atoms, even if the selection is
- * dynamic.
- * Dynamic selections only affect the \p refid field of the indexgroup map
- * given in the positions.
- */
-#define ANA_USE_POSMASK (1<<12)
-/*! \brief
- * Pass the reference groups to gmx_analysisfunc().
- *
- * If this flag is specified, the reference groups are passed on to
- * gmx_analysisfunc().
- * Similarly, the arrays returned by gmx_ana_get_anagrps() and
- * gmx_ana_get_grpnames() contain the reference groups in the beginning.
- */
-#define ANA_USE_FULLGRPS (1<<13)
-/*! \brief
- * Dump the parsed and compiled selection trees.
- *
- * This flag is used by internal debugging tools to make
- * gmx_ana_init_selections() dump the selection trees to stderr.
- */
-#define ANA_DEBUG_SELECTION (1<<16)
-/*@}*/
-
-
-/*! \name Functions for initialization */
-/*@{*/
-
-/** Allocates and initializes data structure for trajectory analysis. */
-int
-gmx_ana_traj_create(gmx_ana_traj_t **data, unsigned long flags);
-/** Frees the memory allocated for trajectory analysis data. */
-void
-gmx_ana_traj_free(gmx_ana_traj_t *d);
-/** Sets additional flags after gmx_ana_traj_create() has been called. */
-int
-gmx_ana_add_flags(gmx_ana_traj_t *d, unsigned long flags);
-/** Sets the number of reference groups required. */
-int
-gmx_ana_set_nrefgrps(gmx_ana_traj_t *d, int nrefgrps);
-/** Sets the number of analysis groups required. */
-int
-gmx_ana_set_nanagrps(gmx_ana_traj_t *d, int nanagrps);
-/** Sets whether PBC are used. */
-int
-gmx_ana_set_pbc(gmx_ana_traj_t *d, gmx_bool bPBC);
-/** Sets whether molecules are made whole. */
-int
-gmx_ana_set_rmpbc(gmx_ana_traj_t *d, gmx_bool bRmPBC);
-/** Sets flags that determine what to read from the trajectory. */
-int
-gmx_ana_set_frflags(gmx_ana_traj_t *d, int frflags);
-/** Parses command-line arguments and performs some initialization. */
-int
-parse_trjana_args(gmx_ana_traj_t *d, int *argc, char *argv[],
- unsigned long pca_flags, int nfile, t_filenm fnm[],
- int npargs, t_pargs *pa,
- int ndesc, const char **desc,
- int nbugs, const char **bugs,
- output_env_t *oenv);
-/** Initializes selection information. */
-int
-gmx_ana_init_selections(gmx_ana_traj_t *d);
-/** Initializes calculation of covered fractions for selections. */
-int
-gmx_ana_init_coverfrac(gmx_ana_traj_t *d, e_coverfrac_t type);
-
-/** Returns whether PBC should be used. */
-gmx_bool
-gmx_ana_has_pbc(gmx_ana_traj_t *d);
-/** Gets the topology information. */
-int
-gmx_ana_get_topology(gmx_ana_traj_t *d, gmx_bool bReq, t_topology **top, gmx_bool *bTop);
-/** Gets the configuration from the topology. */
-int
-gmx_ana_get_topconf(gmx_ana_traj_t *d, rvec **x, matrix box, int *ePBC);
-/** Gets the first frame to be analyzed. */
-int
-gmx_ana_get_first_frame(gmx_ana_traj_t *d, t_trxframe **fr);
-
-/** Gets the total number of selections provided by the user. */
-int
-gmx_ana_get_ngrps(gmx_ana_traj_t *d, int *ngrps);
-/** Gets the number of analysis groups provided by the user. */
-int
-gmx_ana_get_nanagrps(gmx_ana_traj_t *d, int *nanagrps);
-/** Gets the selection object for a reference selection. */
-int
-gmx_ana_get_refsel(gmx_ana_traj_t *d, int i, gmx_ana_selection_t **sel);
-/** Gets the selection object for a reference selection. */
-int
-gmx_ana_get_anagrps(gmx_ana_traj_t *d, gmx_ana_selection_t ***sel);
-/** Gets an array of names for the selections. */
-int
-gmx_ana_get_grpnames(gmx_ana_traj_t *d, char ***grpnames);
-/** Gets the selection collection object that contains all the selections. */
-int
-gmx_ana_get_selcollection(gmx_ana_traj_t *d, gmx_ana_selcollection_t **sc);
-/** Prints the selection strings into an XVGR file as comments. */
-int
-xvgr_selections(FILE *out, gmx_ana_traj_t *d);
-
-/*@}*/
-
-
-/*! \name Functions for reading and analyzing the trajectory
- */
-/*@{*/
-
-/*! \brief
- * Function pointer type for frame analysis functions.
- *
- * \param[in] top Topology structure.
- * \param[in] fr Current frame.
- * \param[in] pbc Initialized PBC structure for this frame.
- * \param[in] nr Number of selections in the \p sel array.
- * \param[in] sel Array of selections.
- * \param data User data as provided to gmx_ana_do().
- * \returns 0 on success, a non-zero error code on error.
- *
- * This function is called by gmx_ana_do() for each frame that
- * needs to be analyzed.
- * Positions to be analyzed can be found in the \p sel array.
- * The selection array \p sel also provides information about the atoms that
- * have been used to evaluate the positions.
- * If a non-zero value is returned, gmx_ana_do() immediately exits and returns
- * the same value to the caller.
- */
-typedef int (*gmx_analysisfunc)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- int nr, gmx_ana_selection_t *sel[], void *data);
-
-/** Loops through all frames in the trajectory. */
-int
-gmx_ana_do(gmx_ana_traj_t *d, int flags, gmx_analysisfunc analyze, void *data);
-/** Gets the total number of frames analyzed. */
-int
-gmx_ana_get_nframes(gmx_ana_traj_t *d, int *nframes);
-
-/*@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-Makefile.in
-Makefile
\ No newline at end of file
+++ /dev/null
-## Process this file with automake to produce Makefile.in
-#
-# Don't edit - this file is generated automatically from Makefile.am
-#
-pkgincludetypesdir = ${pkgincludedir}/types
-
-pkgincludetypes_HEADERS = \
- atoms.h forcerec.h ifunc.h oenv.h \
- mdatom.h simple.h block.h constr.h \
- energy.h graph.h inputrec.h nblist.h \
- ns.h nsgrid.h symtab.h commrec.h \
- enums.h group.h ishift.h \
- topology.h fcdata.h filenm.h idef.h \
- matrix.h nrnb.h trx.h state.h \
- pbc.h qmmmrec.h shellfc.h genborn.h
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GRoups of Organic Molecules in ACtion for Science
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* note: these enums should correspond to the names in gmxlib/names.c */
-
-enum {
- epbcXYZ, epbcNONE, epbcXY, epbcSCREW, epbcNR
-};
-
-enum {
- etcNO, etcBERENDSEN, etcNOSEHOOVER, etcYES, etcANDERSEN, etcANDERSENINTERVAL, etcVRESCALE, etcNR
-}; /* yes is an alias for berendsen */
-
-enum {
- epcNO, epcBERENDSEN, epcPARRINELLORAHMAN, epcISOTROPIC, epcMTTK, epcNR
-}; /* isotropic is an alias for berendsen */
-
-/* trotter decomposition extended variable parts */
-enum {
- etrtNONE, etrtNHC, etrtBAROV, etrtBARONHC, etrtNHC2, etrtBAROV2, etrtBARONHC2,
- etrtVELOCITY1, etrtVELOCITY2, etrtPOSITION, etrtSKIPALL, etrtNR
-};
-
-/* sequenced parts of the trotter decomposition */
-enum {
- ettTSEQ0, ettTSEQ1, ettTSEQ2, ettTSEQ3, ettTSEQ4, ettTSEQMAX
-};
-
-enum {
- epctISOTROPIC, epctSEMIISOTROPIC, epctANISOTROPIC,
- epctSURFACETENSION, epctNR
-};
-
-enum {
- erscNO, erscALL, erscCOM, erscNR
-};
-
-/*
- * eelNOTUSED1 used to be GB, but to enable generalized born with different
- * forms of electrostatics (RF, switch, etc.) in the future it is now selected
- * separately (through the implicit_solvent option).
- */
-enum {
- eelCUT, eelRF, eelGRF, eelPME, eelEWALD, eelPPPM,
- eelPOISSON, eelSWITCH, eelSHIFT, eelUSER, eelGB_NOTUSED, eelRF_NEC, eelENCADSHIFT,
- eelPMEUSER, eelPMESWITCH, eelPMEUSERSWITCH, eelRF_ZERO, eelNR
-};
-
-/* Ewald geometry */
-enum {
- eewg3D, eewg3DC, eewgNR
-};
-
-#define EEL_RF(e) ((e) == eelRF || (e) == eelGRF || (e) == eelRF_NEC || (e) == eelRF_ZERO )
-
-#define EEL_PME(e) ((e) == eelPME || (e) == eelPMESWITCH || (e) == eelPMEUSER || (e) == eelPMEUSERSWITCH)
-#define EEL_FULL(e) (EEL_PME(e) || (e) == eelPPPM || (e) == eelPOISSON || (e) == eelEWALD)
-
-#define EEL_SWITCHED(e) ((e) == eelSWITCH || (e) == eelSHIFT || (e) == eelENCADSHIFT || (e) == eelPMESWITCH || (e) == eelPMEUSERSWITCH)
-
-#define EEL_IS_ZERO_AT_CUTOFF(e) (EEL_SWITCHED(e) || (e) == eelRF_ZERO)
-
-#define EEL_MIGHT_BE_ZERO_AT_CUTOFF(e) (EEL_IS_ZERO_AT_CUTOFF(e) || (e) == eelUSER || (e) == eelPMEUSER)
-
-enum {
- evdwCUT, evdwSWITCH, evdwSHIFT, evdwUSER, evdwENCADSHIFT, evdwNR
-};
-
-#define EVDW_SWITCHED(e) ((e) == evdwSWITCH || (e) == evdwSHIFT || (e) == evdwENCADSHIFT)
-
-#define EVDW_IS_ZERO_AT_CUTOFF(e) EVDW_SWITCHED(e)
-
-#define EVDW_MIGHT_BE_ZERO_AT_CUTOFF(e) (EVDW_IS_ZERO_AT_CUTOFF(e) || (e) == evdwUSER)
-
-enum {
- ensGRID, ensSIMPLE, ensNR
-};
-
-/* eiVV is normal velocity verlet -- eiVVAK uses 1/2*(KE(t-dt/2)+KE(t+dt/2)) as the kinetic energy, and the half step kinetic
- energy for temperature control */
-
-enum {
- eiMD, eiSteep, eiCG, eiBD, eiSD2, eiNM, eiLBFGS, eiTPI, eiTPIC, eiSD1, eiVV, eiVVAK, eiNR
-};
-#define EI_VV(e) ((e) == eiVV || (e) == eiVVAK)
-#define EI_SD(e) ((e) == eiSD1 || (e) == eiSD2)
-#define EI_RANDOM(e) (EI_SD(e) || (e) == eiBD)
-/*above integrators may not conserve momenta*/
-#define EI_DYNAMICS(e) ((e) == eiMD || EI_SD(e) || (e) == eiBD || EI_VV(e))
-#define EI_ENERGY_MINIMIZATION(e) ((e) == eiSteep || (e) == eiCG || (e) == eiLBFGS)
-#define EI_TPI(e) ((e) == eiTPI || (e) == eiTPIC)
-
-#define EI_STATE_VELOCITY(e) ((e) == eiMD || EI_VV(e) || EI_SD(e))
-
-enum {
- econtLINCS, econtSHAKE, econtNR
-};
-
-enum {
- edrNone, edrSimple, edrEnsemble, edrNR
-};
-
-enum {
- edrwConservative, edrwEqual, edrwNR
-};
-
-/* Combination rule things */
-enum {
- eCOMB_NONE, eCOMB_GEOMETRIC, eCOMB_ARITHMETIC, eCOMB_GEOM_SIG_EPS, eCOMB_NR
-};
-
-/* NBF selection */
-enum {
- eNBF_NONE, eNBF_LJ, eNBF_BHAM, eNBF_NR
-};
-
-/* FEP selection */
-enum {
- efepNO, efepYES, efepNR
-};
-
-/* separate_dhdl_file selection */
-enum
-{
- /* NOTE: YES is the first one. Do NOT interpret this one as a gmx_bool */
- sepdhdlfileYES, sepdhdlfileNO, sepdhdlfileNR
-};
-
-/* dhdl_derivatives selection */
-enum
-{
- /* NOTE: YES is the first one. Do NOT interpret this one as a gmx_bool */
- dhdlderivativesYES, dhdlderivativesNO, dhdlderivativesNR
-};
-
-/* Solvent model */
-enum {
- esolNO, esolSPC, esolTIP4P, esolNR
-};
-
-/* Dispersion correction */
-enum {
- edispcNO, edispcEnerPres, edispcEner, edispcAllEnerPres, edispcAllEner, edispcNR
-};
-
-/* Shell types, for completion stuff */
-enum {
- eshellCSH, eshellBASH, eshellZSH, eshellNR
-};
-
-/* Center of mass motion selection */
-enum {
- ecmLINEAR, ecmANGULAR, ecmNO, ecmNR
-};
-
-/* New version of simulated annealing */
-enum {
- eannNO, eannSINGLE, eannPERIODIC, eannNR
-};
-
-/* Implicit solvent algorithms */
-enum {
- eisNO, eisGBSA, eisNR
-};
-
-/* Algorithms for calculating GB radii */
-enum {
- egbSTILL, egbHCT, egbOBC, egbNR
-};
-
-enum {
- esaAPPROX, esaNO, esaSTILL, esaNR
-};
-
-/* Wall types */
-enum {
- ewt93, ewt104, ewtTABLE, ewt126, ewtNR
-};
-
-/* Pull stuff */
-enum {
- epullNO, epullUMBRELLA, epullCONSTRAINT, epullCONST_F, epullNR
-};
-
-enum {
- epullgDIST, epullgDIR, epullgCYL, epullgPOS, epullgDIRPBC, epullgNR
-};
-
-#define PULL_CYL(pull) ((pull)->eGeom == epullgCYL)
-
-/* QMMM */
-enum {
- eQMmethodAM1, eQMmethodPM3, eQMmethodRHF,
- eQMmethodUHF, eQMmethodDFT, eQMmethodB3LYP, eQMmethodMP2, eQMmethodCASSCF, eQMmethodB3LYPLAN,
- eQMmethodDIRECT, eQMmethodNR
-};
-
-enum {
- eQMbasisSTO3G, eQMbasisSTO3G2, eQMbasis321G,
- eQMbasis321Gp, eQMbasis321dGp, eQMbasis621G,
- eQMbasis631G, eQMbasis631Gp, eQMbasis631dGp,
- eQMbasis6311G, eQMbasisNR
-};
-
-enum {
- eQMMMschemenormal,eQMMMschemeoniom,eQMMMschemeNR
-};
-
-enum {
- eMultentOptName, eMultentOptNo, eMultentOptLast, eMultentOptNR
-};
-
-#ifdef __cplusplus
-}
-#endif
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GRoups of Organic Molecules in ACtion for Science
- */
-#ifndef _inputrec_h_
-#define _inputrec_h_
-
-
-#include "simple.h"
-#include "../sysstuff.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-typedef struct {
- int n; /* Number of terms */
- real *a; /* Coeffients (V / nm ) */
- real *phi; /* Phase angles */
-} t_cosines;
-
-typedef struct {
- real E0; /* Field strength (V/nm) */
- real omega; /* Frequency (1/ps) */
- real t0; /* Centre of the Gaussian pulse (ps) */
- real sigma; /* Width of the Gaussian pulse (FWHM) (ps) */
-} t_efield;
-
-#define EGP_EXCL (1<<0)
-#define EGP_TABLE (1<<1)
-
-typedef struct {
- int ngtc; /* # T-Coupl groups */
- int nhchainlength; /* # of nose-hoover chains per group */
- int ngacc; /* # Accelerate groups */
- int ngfrz; /* # Freeze groups */
- int ngener; /* # Ener groups */
- real *nrdf; /* Nr of degrees of freedom in a group */
- real *ref_t; /* Coupling temperature per group */
- int *annealing; /* No/simple/periodic SA for each group */
- int *anneal_npoints; /* Number of annealing time points per grp */
- real **anneal_time; /* For ea. group: Time points */
- real **anneal_temp; /* For ea. grp: Temperature at these times */
- /* Final temp after all intervals is ref_t */
- real *tau_t; /* Tau coupling time */
- rvec *acc; /* Acceleration per group */
- ivec *nFreeze; /* Freeze the group in each direction ? */
- int *egp_flags; /* Exclusions/tables of energy group pairs */
-
- /* QMMM stuff */
- int ngQM; /* nr of QM groups */
- int *QMmethod; /* Level of theory in the QM calculation */
- int *QMbasis; /* Basisset in the QM calculation */
- int *QMcharge; /* Total charge in the QM region */
- int *QMmult; /* Spin multiplicicty in the QM region */
- gmx_bool *bSH; /* surface hopping (diabatic hop only) */
- int *CASorbitals; /* number of orbiatls in the active space */
- int *CASelectrons;/* number of electrons in the active space */
- real *SAon; /* at which gap (A.U.) the SA is switched on */
- real *SAoff;
- int *SAsteps; /* in how many steps SA goes from 1-1 to 0.5-0.5*/
- gmx_bool *bOPT;
- gmx_bool *bTS;
-} t_grpopts;
-
-enum { epgrppbcNONE, epgrppbcREFAT, epgrppbcCOS };
-
-typedef struct {
- int nat; /* Number of atoms in the pull group */
- atom_id *ind; /* The global atoms numbers */
- int nat_loc; /* Number of local pull atoms */
- int nalloc_loc; /* Allocation size for ind_loc and weight_loc */
- atom_id *ind_loc; /* Local pull indices */
- int nweight; /* The number of weights (0 or nat) */
- real *weight; /* Weights (use all 1 when weight==NULL) */
- real *weight_loc; /* Weights for the local indices */
- int epgrppbc; /* The type of pbc for this pull group, see enum above */
- atom_id pbcatom; /* The reference atom for pbc (global number) */
- rvec vec; /* The pull vector, direction or position */
- rvec init; /* Initial reference displacement */
- real rate; /* Rate of motion (nm/ps) */
- real k; /* force constant */
- real kB; /* force constant for state B */
- real wscale; /* scaling factor for the weights: sum w m/sum w w m */
- real invtm; /* inverse total mass of the group: 1/wscale sum w m */
- dvec x; /* center of mass before update */
- dvec xp; /* center of mass after update before constraining */
- dvec dr; /* The distance from the reference group */
- double f_scal; /* Scalar force for directional pulling */
- dvec f; /* force due to the pulling/constraining */
-} t_pullgrp;
-
-typedef struct {
- int ngrp; /* number of groups */
- int eGeom; /* pull geometry */
- ivec dim; /* used to select components for constraint */
- real cyl_r1; /* radius of cylinder for dynamic COM */
- real cyl_r0; /* radius of cylinder including switch length */
- real constr_tol; /* absolute tolerance for constraints in (nm) */
- int nstxout; /* Output frequency for pull x */
- int nstfout; /* Output frequency for pull f */
- int ePBC; /* the boundary conditions */
- int npbcdim; /* do pbc in dims 0 <= dim < npbcdim */
- gmx_bool bRefAt; /* do we need reference atoms for a group COM ? */
- int cosdim; /* dimension for cosine weighting, -1 if none */
- gmx_bool bVirial; /* do we need to add the pull virial? */
- t_pullgrp *grp; /* groups to pull/restrain/etc/ */
- t_pullgrp *dyna; /* dynamic groups for use with local constraints */
- rvec *rbuf; /* COM calculation buffer */
- dvec *dbuf; /* COM calculation buffer */
- double *dbuf_cyl; /* cylinder ref. groups COM calculation buffer */
-
- FILE *out_x; /* output file for pull data */
- FILE *out_f; /* output file for pull data */
-} t_pull;
-
-typedef struct {
- int eI; /* Integration method */
- gmx_large_int_t nsteps; /* number of steps to be taken */
- int simulation_part; /* Used in checkpointing to separate chunks */
- gmx_large_int_t init_step; /* start at a stepcount >0 (used w. tpbconv) */
- int nstcalcenergy; /* fequency of energy calc. and T/P coupl. upd. */
- int ns_type; /* which ns method should we use? */
- int nstlist; /* number of steps before pairlist is generated */
- int ndelta; /* number of cells per rlong */
- int nstcomm; /* number of steps after which center of mass */
- /* motion is removed */
- int comm_mode; /* Center of mass motion removal algorithm */
- int nstcheckpoint; /* checkpointing frequency */
- int nstlog; /* number of steps after which print to logfile */
- int nstxout; /* number of steps after which X is output */
- int nstvout; /* id. for V */
- int nstfout; /* id. for F */
- int nstenergy; /* number of steps after which energies printed */
- int nstxtcout; /* id. for compressed trj (.xtc) */
- double init_t; /* initial time (ps) */
- double delta_t; /* time step (ps) */
- real xtcprec; /* precision of xtc file */
- int nkx,nky,nkz; /* number of k vectors in each spatial dimension*/
- /* for fourier methods for long range electrost.*/
- int pme_order; /* interpolation order for PME */
- real ewald_rtol; /* Real space tolerance for Ewald, determines */
- /* the real/reciprocal space relative weight */
- int ewald_geometry; /* normal/3d ewald, or pseudo-2d LR corrections */
- real epsilon_surface; /* Epsilon for PME dipole correction */
- gmx_bool bOptFFT; /* optimize the fft plan at start */
- int ePBC; /* Type of periodic boundary conditions */
- int bPeriodicMols; /* Periodic molecules */
- gmx_bool bContinuation; /* Continuation run: starting state is correct */
- int etc; /* temperature coupling */
- int nsttcouple; /* interval in steps for temperature coupling */
- int epc; /* pressure coupling */
- int epct; /* pressure coupling type */
- int nstpcouple; /* interval in steps for pressure coupling */
- real tau_p; /* pressure coupling time (ps) */
- tensor ref_p; /* reference pressure (kJ/(mol nm^3)) */
- tensor compress; /* compressability ((mol nm^3)/kJ) */
- int refcoord_scaling;/* How to scale absolute reference coordinates */
- rvec posres_com; /* The COM of the posres atoms */
- rvec posres_comB; /* The B-state COM of the posres atoms */
- int andersen_seed; /* Random seed for Andersen thermostat. */
- real rlist; /* short range pairlist cut-off (nm) */
- real rlistlong; /* long range pairlist cut-off (nm) */
- real rtpi; /* Radius for test particle insertion */
- int coulombtype; /* Type of electrostatics treatment */
- real rcoulomb_switch; /* Coulomb switch range start (nm) */
- real rcoulomb; /* Coulomb cutoff (nm) */
- real epsilon_r; /* relative dielectric constant */
- real epsilon_rf; /* relative dielectric constant of the RF */
- int implicit_solvent;/* No (=explicit water), or GBSA solvent models */
- int gb_algorithm; /* Algorithm to use for calculation Born radii */
- int nstgbradii; /* Frequency of updating Generalized Born radii */
- real rgbradii; /* Cutoff for GB radii calculation */
- real gb_saltconc; /* Salt concentration (M) for GBSA models */
- real gb_epsilon_solvent; /* dielectric coeff. of implicit solvent */
- real gb_obc_alpha; /* 1st scaling factor for Bashford-Case GB */
- real gb_obc_beta; /* 2nd scaling factor for Bashford-Case GB */
- real gb_obc_gamma; /* 3rd scaling factor for Bashford-Case GB */
- real gb_dielectric_offset; /* Dielectric offset for Still/HCT/OBC */
- int sa_algorithm; /* Algorithm for SA part of GBSA */
- real sa_surface_tension; /* Energy factor for SA part of GBSA */
- int vdwtype; /* Type of Van der Waals treatment */
- real rvdw_switch; /* Van der Waals switch range start (nm) */
- real rvdw; /* Van der Waals cutoff (nm) */
- int eDispCorr; /* Perform Long range dispersion corrections */
- real tabext; /* Extension of the table beyond the cut-off, *
- * as well as the table length for 1-4 interac. */
- real shake_tol; /* tolerance for shake */
- int efep; /* free energy interpolation no/yes */
- double init_lambda; /* initial value for perturbation variable */
- double delta_lambda; /* change of lambda per time step (1/dt) */
- int n_flambda; /* The number of foreign lambda points */
- double *flambda; /* The foreign lambda values */
- real sc_alpha; /* free energy soft-core parameter */
- int sc_power; /* lambda power for soft-core interactions */
- real sc_sigma; /* free energy soft-core sigma when c6 or c12=0 */
- real sc_sigma_min; /* minimum FE sc sigma (default: =sg_sigma) */
- int nstdhdl; /* The frequency for writing to dhdl.xvg */
- int separate_dhdl_file; /* whether to write a separate dhdl.xvg file
- note: NOT a gmx_bool, but an enum */
- int dhdl_derivatives;/* whether to calculate+write dhdl derivatives
- note: NOT a gmx_bool, but an enum */
- int dh_hist_size; /* The maximum size for the dH histogram */
- double dh_hist_spacing; /* The spacing for the dH histogram */
- int eDisre; /* Type of distance restraining */
- real dr_fc; /* force constant for ta_disre */
- int eDisreWeighting; /* type of weighting of pairs in one restraints */
- gmx_bool bDisreMixed; /* Use comb of time averaged and instan. viol's */
- int nstdisreout; /* frequency of writing pair distances to enx */
- real dr_tau; /* time constant for memory function in disres */
- real orires_fc; /* force constant for orientational restraints */
- real orires_tau; /* time constant for memory function in orires */
- int nstorireout; /* frequency of writing tr(SD) to enx */
- real dihre_fc; /* force constant for dihedral restraints */
- real em_stepsize; /* The stepsize for updating */
- real em_tol; /* The tolerance */
- int niter; /* Number of iterations for convergence of */
- /* steepest descent in relax_shells */
- real fc_stepsize; /* Stepsize for directional minimization */
- /* in relax_shells */
- int nstcgsteep; /* number of steps after which a steepest */
- /* descents step is done while doing cg */
- int nbfgscorr; /* Number of corrections to the hessian to keep */
- int eConstrAlg; /* Type of constraint algorithm */
- int nProjOrder; /* Order of the LINCS Projection Algorithm */
- real LincsWarnAngle; /* If bond rotates more than %g degrees, warn */
- int nLincsIter; /* Number of iterations in the final Lincs step */
- gmx_bool bShakeSOR; /* Use successive overrelaxation for shake */
- real bd_fric; /* Friction coefficient for BD (amu/ps) */
- int ld_seed; /* Random seed for SD and BD */
- int nwall; /* The number of walls */
- int wall_type; /* The type of walls */
- real wall_r_linpot; /* The potentail is linear for r<=wall_r_linpot */
- int wall_atomtype[2];/* The atom type for walls */
- real wall_density[2]; /* Number density for walls */
- real wall_ewald_zfac; /* Scaling factor for the box for Ewald */
- int ePull; /* Type of pulling: no, umbrella or constraint */
- t_pull *pull; /* The data for center of mass pulling */
- real cos_accel; /* Acceleration for viscosity calculation */
- tensor deform; /* Triclinic deformation velocities (nm/ps) */
- int userint1; /* User determined parameters */
- int userint2;
- int userint3;
- int userint4;
- real userreal1;
- real userreal2;
- real userreal3;
- real userreal4;
- t_grpopts opts; /* Group options */
- t_cosines ex[DIM]; /* Electric field stuff (spatial part) */
- t_cosines et[DIM]; /* Electric field stuff (time part) */
- gmx_bool bQMMM; /* QM/MM calculation */
- int QMconstraints; /* constraints on QM bonds */
- int QMMMscheme; /* Scheme: ONIOM or normal */
- real scalefactor; /* factor for scaling the MM charges in QM calc.*/
-} t_inputrec;
-
-#define DEFORM(ir) ((ir).deform[XX][XX]!=0 || (ir).deform[YY][YY]!=0 || (ir).deform[ZZ][ZZ]!=0 || (ir).deform[YY][XX]!=0 || (ir).deform[ZZ][XX]!=0 || (ir).deform[ZZ][YY]!=0)
-
-#define DYNAMIC_BOX(ir) ((ir).epc!=epcNO || (ir).eI==eiTPI || DEFORM(ir))
-
-#define PRESERVE_SHAPE(ir) ((ir).epc != epcNO && (ir).deform[XX][XX] == 0 && ((ir).epct == epctISOTROPIC || (ir).epct == epctSEMIISOTROPIC))
-
-#define NEED_MUTOT(ir) (((ir).coulombtype==eelEWALD || EEL_PME((ir).coulombtype)) && ((ir).ewald_geometry==eewg3DC || (ir).epsilon_surface!=0))
-
-#define IR_TWINRANGE(ir) ((ir).rlist > 0 && ((ir).rlistlong == 0 || (ir).rlistlong > (ir).rlist))
-
-#define IR_ELEC_FIELD(ir) ((ir).ex[XX].n > 0 || (ir).ex[YY].n > 0 || (ir).ex[ZZ].n > 0)
-
-#define IR_EXCL_FORCES(ir) (EEL_FULL((ir).coulombtype) || (EEL_RF((ir).coulombtype) && (ir).coulombtype != eelRF_NEC) || (ir).implicit_solvent != eisNO)
-/* use pointer definitions of ir here, since that's what's usually used in the code */
-#define IR_NVT_TROTTER(ir) ((((ir)->eI == eiVV) || ((ir)->eI == eiVVAK)) && ((ir)->etc == etcNOSEHOOVER))
-
-#define IR_NPT_TROTTER(ir) ((((ir)->eI == eiVV) || ((ir)->eI == eiVVAK)) && ((ir)->epc == epcMTTK))
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gromacs Runs On Most of All Computer Systems
- */
-#ifndef _vec_h
-#define _vec_h
-
-/*
- collection of in-line ready operations:
-
- lookup-table optimized scalar operations:
- real gmx_invsqrt(real x)
- void vecinvsqrt(real in[],real out[],int n)
- void vecrecip(real in[],real out[],int n)
- real sqr(real x)
- double dsqr(double x)
-
- vector operations:
- void rvec_add(const rvec a,const rvec b,rvec c) c = a + b
- void dvec_add(const dvec a,const dvec b,dvec c) c = a + b
- void ivec_add(const ivec a,const ivec b,ivec c) c = a + b
- void rvec_inc(rvec a,const rvec b) a += b
- void dvec_inc(dvec a,const dvec b) a += b
- void ivec_inc(ivec a,const ivec b) a += b
- void rvec_sub(const rvec a,const rvec b,rvec c) c = a - b
- void dvec_sub(const dvec a,const dvec b,dvec c) c = a - b
- void rvec_dec(rvec a,rvec b) a -= b
- void copy_rvec(const rvec a,rvec b) b = a (reals)
- void copy_dvec(const dvec a,dvec b) b = a (reals)
- void copy_ivec(const ivec a,ivec b) b = a (integers)
- void ivec_sub(const ivec a,const ivec b,ivec c) c = a - b
- void svmul(real a,rvec v1,rvec v2) v2 = a * v1
- void dsvmul(double a,dvec v1,dvec v2) v2 = a * v1
- void clear_rvec(rvec a) a = 0
- void clear_dvec(dvec a) a = 0
- void clear_ivec(rvec a) a = 0
- void clear_rvecs(int n,rvec v[])
- real iprod(rvec a,rvec b) = a . b (inner product)
- double diprod(dvec a,dvec b) = a . b (inner product)
- real iiprod(ivec a,ivec b) = a . b (integers)
- real norm2(rvec a) = | a |^2 ( = x*y*z )
- double dnorm2(dvec a) = | a |^2 ( = x*y*z )
- real norm(rvec a) = | a |
- double dnorm(dvec a) = | a |
- void cprod(rvec a,rvec b,rvec c) c = a x b (cross product)
- void dprod(rvec a,rvec b,rvec c) c = a x b (cross product)
- void dprod(rvec a,rvec b,rvec c) c = a * b (direct product)
- real cos_angle(rvec a,rvec b)
- real cos_angle_no_table(rvec a,rvec b)
- real distance2(rvec v1, rvec v2) = | v2 - v1 |^2
- void unitv(rvec src,rvec dest) dest = src / |src|
- void unitv_no_table(rvec src,rvec dest) dest = src / |src|
-
- matrix (3x3) operations:
- ! indicates that dest should not be the same as a, b or src
- the _ur0 varieties work on matrices that have only zeros
- in the upper right part, such as box matrices, these varieties
- could produce less rounding errors, not due to the operations themselves,
- but because the compiler can easier recombine the operations
- void copy_mat(matrix a,matrix b) b = a
- void clear_mat(matrix a) a = 0
- void mmul(matrix a,matrix b,matrix dest) ! dest = a . b
- void mmul_ur0(matrix a,matrix b,matrix dest) dest = a . b
- void transpose(matrix src,matrix dest) ! dest = src*
- void tmmul(matrix a,matrix b,matrix dest) ! dest = a* . b
- void mtmul(matrix a,matrix b,matrix dest) ! dest = a . b*
- real det(matrix a) = det(a)
- void m_add(matrix a,matrix b,matrix dest) dest = a + b
- void m_sub(matrix a,matrix b,matrix dest) dest = a - b
- void msmul(matrix m1,real r1,matrix dest) dest = r1 * m1
- void m_inv_ur0(matrix src,matrix dest) dest = src^-1
- void m_inv(matrix src,matrix dest) ! dest = src^-1
- void mvmul(matrix a,rvec src,rvec dest) ! dest = a . src
- void mvmul_ur0(matrix a,rvec src,rvec dest) dest = a . src
- void tmvmul_ur0(matrix a,rvec src,rvec dest) dest = a* . src
- real trace(matrix m) = trace(m)
-*/
-
-#include "types/simple.h"
-#include "maths.h"
-#include "typedefs.h"
-#include "sysstuff.h"
-#include "macros.h"
-#include "gmx_fatal.h"
-#include "mpelogging.h"
-#include "physics.h"
-
-#ifdef __cplusplus
-extern "C" {
-#elif 0
-} /* avoid screwing up indentation */
-#endif
-
-
-#define EXP_LSB 0x00800000
-#define EXP_MASK 0x7f800000
-#define EXP_SHIFT 23
-#define FRACT_MASK 0x007fffff
-#define FRACT_SIZE 11 /* significant part of fraction */
-#define FRACT_SHIFT (EXP_SHIFT-FRACT_SIZE)
-#define EXP_ADDR(val) (((val)&EXP_MASK)>>EXP_SHIFT)
-#define FRACT_ADDR(val) (((val)&(FRACT_MASK|EXP_LSB))>>FRACT_SHIFT)
-
-#define PR_VEC(a) a[XX],a[YY],a[ZZ]
-
-#ifdef GMX_SOFTWARE_INVSQRT
-extern const unsigned int * gmx_invsqrt_exptab;
-extern const unsigned int * gmx_invsqrt_fracttab;
-#endif
-
-
-typedef union
-{
- unsigned int bval;
- float fval;
-} t_convert;
-
-
-#ifdef GMX_SOFTWARE_INVSQRT
-static real gmx_invsqrt(real x)
-{
- const real half=0.5;
- const real three=3.0;
- t_convert result,bit_pattern;
- unsigned int exp,fract;
- real lu;
- real y;
-#ifdef GMX_DOUBLE
- real y2;
-#endif
-
- bit_pattern.fval=x;
- exp = EXP_ADDR(bit_pattern.bval);
- fract = FRACT_ADDR(bit_pattern.bval);
- result.bval=gmx_invsqrt_exptab[exp] | gmx_invsqrt_fracttab[fract];
- lu = result.fval;
-
- y=(half*lu*(three-((x*lu)*lu)));
-#ifdef GMX_DOUBLE
- y2=(half*y*(three-((x*y)*y)));
-
- return y2; /* 10 Flops */
-#else
- return y; /* 5 Flops */
-#endif
-}
-#define INVSQRT_DONE
-#endif /* gmx_invsqrt */
-
-#ifdef GMX_POWERPC_SQRT
-static real gmx_invsqrt(real x)
-{
- const real half=0.5;
- const real three=3.0;
- t_convert result,bit_pattern;
- unsigned int exp,fract;
- real lu;
- real y;
-#ifdef GMX_DOUBLE
- real y2;
-#endif
-
- lu = __frsqrte((double)x);
-
- y=(half*lu*(three-((x*lu)*lu)));
-
-#if (GMX_POWERPC_SQRT==2)
- /* Extra iteration required */
- y=(half*y*(three-((x*y)*y)));
-#endif
-
-#ifdef GMX_DOUBLE
- y2=(half*y*(three-((x*y)*y)));
-
- return y2; /* 10 Flops */
-#else
- return y; /* 5 Flops */
-#endif
-}
-#define INVSQRT_DONE
-#endif /* powerpc_invsqrt */
-
-
-#ifndef INVSQRT_DONE
-#define gmx_invsqrt(x) (1.0f/sqrt(x))
-#endif
-
-
-
-
-
-static real sqr(real x)
-{
- return (x*x);
-}
-
-static gmx_inline double dsqr(double x)
-{
- return (x*x);
-}
-
-/* Maclaurin series for sinh(x)/x, useful for NH chains and MTTK pressure control
- Here, we compute it to 10th order, which might be overkill, 8th is probably enough,
- but it's not very much more expensive. */
-
-static gmx_inline real series_sinhx(real x)
-{
- real x2 = x*x;
- return (1 + (x2/6.0)*(1 + (x2/20.0)*(1 + (x2/42.0)*(1 + (x2/72.0)*(1 + (x2/110.0))))));
-}
-
-void vecinvsqrt(real in[],real out[],int n);
-/* Perform out[i]=1.0/sqrt(in[i]) for n elements */
-
-
-void vecrecip(real in[],real out[],int n);
-/* Perform out[i]=1.0/(in[i]) for n elements */
-
-/* Note: If you need a fast version of vecinvsqrt
- * and/or vecrecip, call detectcpu() and run the SSE/3DNow/SSE2/Altivec
- * versions if your hardware supports it.
- *
- * To use those routines, your memory HAS TO BE CACHE-ALIGNED.
- * Use snew_aligned(ptr,size,32) to allocate and sfree_aligned to free.
- */
-
-
-static gmx_inline void rvec_add(const rvec a,const rvec b,rvec c)
-{
- real x,y,z;
-
- x=a[XX]+b[XX];
- y=a[YY]+b[YY];
- z=a[ZZ]+b[ZZ];
-
- c[XX]=x;
- c[YY]=y;
- c[ZZ]=z;
-}
-
-static gmx_inline void dvec_add(const dvec a,const dvec b,dvec c)
-{
- double x,y,z;
-
- x=a[XX]+b[XX];
- y=a[YY]+b[YY];
- z=a[ZZ]+b[ZZ];
-
- c[XX]=x;
- c[YY]=y;
- c[ZZ]=z;
-}
-
-static gmx_inline void ivec_add(const ivec a,const ivec b,ivec c)
-{
- int x,y,z;
-
- x=a[XX]+b[XX];
- y=a[YY]+b[YY];
- z=a[ZZ]+b[ZZ];
-
- c[XX]=x;
- c[YY]=y;
- c[ZZ]=z;
-}
-
-static gmx_inline void rvec_inc(rvec a,const rvec b)
-{
- real x,y,z;
-
- x=a[XX]+b[XX];
- y=a[YY]+b[YY];
- z=a[ZZ]+b[ZZ];
-
- a[XX]=x;
- a[YY]=y;
- a[ZZ]=z;
-}
-
-static gmx_inline void dvec_inc(dvec a,const dvec b)
-{
- double x,y,z;
-
- x=a[XX]+b[XX];
- y=a[YY]+b[YY];
- z=a[ZZ]+b[ZZ];
-
- a[XX]=x;
- a[YY]=y;
- a[ZZ]=z;
-}
-
-static gmx_inline void rvec_sub(const rvec a,const rvec b,rvec c)
-{
- real x,y,z;
-
- x=a[XX]-b[XX];
- y=a[YY]-b[YY];
- z=a[ZZ]-b[ZZ];
-
- c[XX]=x;
- c[YY]=y;
- c[ZZ]=z;
-}
-
-static gmx_inline void dvec_sub(const dvec a,const dvec b,dvec c)
-{
- double x,y,z;
-
- x=a[XX]-b[XX];
- y=a[YY]-b[YY];
- z=a[ZZ]-b[ZZ];
-
- c[XX]=x;
- c[YY]=y;
- c[ZZ]=z;
-}
-
-static gmx_inline void rvec_dec(rvec a,const rvec b)
-{
- real x,y,z;
-
- x=a[XX]-b[XX];
- y=a[YY]-b[YY];
- z=a[ZZ]-b[ZZ];
-
- a[XX]=x;
- a[YY]=y;
- a[ZZ]=z;
-}
-
-static gmx_inline void copy_rvec(const rvec a,rvec b)
-{
- b[XX]=a[XX];
- b[YY]=a[YY];
- b[ZZ]=a[ZZ];
-}
-
-static gmx_inline void copy_rvecn(rvec *a,rvec *b,int startn, int endn)
-{
- int i;
- for (i=startn;i<endn;i++) {
- b[i][XX]=a[i][XX];
- b[i][YY]=a[i][YY];
- b[i][ZZ]=a[i][ZZ];
- }
-}
-
-static gmx_inline void copy_dvec(const dvec a,dvec b)
-{
- b[XX]=a[XX];
- b[YY]=a[YY];
- b[ZZ]=a[ZZ];
-}
-
-static gmx_inline void copy_ivec(const ivec a,ivec b)
-{
- b[XX]=a[XX];
- b[YY]=a[YY];
- b[ZZ]=a[ZZ];
-}
-
-static gmx_inline void ivec_sub(const ivec a,const ivec b,ivec c)
-{
- int x,y,z;
-
- x=a[XX]-b[XX];
- y=a[YY]-b[YY];
- z=a[ZZ]-b[ZZ];
-
- c[XX]=x;
- c[YY]=y;
- c[ZZ]=z;
-}
-
-static gmx_inline void copy_mat(matrix a,matrix b)
-{
- copy_rvec(a[XX],b[XX]);
- copy_rvec(a[YY],b[YY]);
- copy_rvec(a[ZZ],b[ZZ]);
-}
-
-static gmx_inline void svmul(real a,const rvec v1,rvec v2)
-{
- v2[XX]=a*v1[XX];
- v2[YY]=a*v1[YY];
- v2[ZZ]=a*v1[ZZ];
-}
-
-static gmx_inline void dsvmul(double a,const dvec v1,dvec v2)
-{
- v2[XX]=a*v1[XX];
- v2[YY]=a*v1[YY];
- v2[ZZ]=a*v1[ZZ];
-}
-
-static gmx_inline real distance2(const rvec v1,const rvec v2)
-{
- return sqr(v2[XX]-v1[XX]) + sqr(v2[YY]-v1[YY]) + sqr(v2[ZZ]-v1[ZZ]);
-}
-
-static gmx_inline void clear_rvec(rvec a)
-{
- /* The ibm compiler has problems with inlining this
- * when we use a const real variable
- */
- a[XX]=0.0;
- a[YY]=0.0;
- a[ZZ]=0.0;
-}
-
-static gmx_inline void clear_dvec(dvec a)
-{
- /* The ibm compiler has problems with inlining this
- * when we use a const real variable
- */
- a[XX]=0.0;
- a[YY]=0.0;
- a[ZZ]=0.0;
-}
-
-static gmx_inline void clear_ivec(ivec a)
-{
- a[XX]=0;
- a[YY]=0;
- a[ZZ]=0;
-}
-
-static gmx_inline void clear_rvecs(int n,rvec v[])
-{
-/* memset(v[0],0,DIM*n*sizeof(v[0][0])); */
- int i;
-
- GMX_MPE_LOG(ev_clear_rvecs_start);
-
- for(i=0; (i<n); i++)
- clear_rvec(v[i]);
-
- GMX_MPE_LOG(ev_clear_rvecs_finish);
-}
-
-static gmx_inline void clear_mat(matrix a)
-{
-/* memset(a[0],0,DIM*DIM*sizeof(a[0][0])); */
-
- const real nul=0.0;
-
- a[XX][XX]=a[XX][YY]=a[XX][ZZ]=nul;
- a[YY][XX]=a[YY][YY]=a[YY][ZZ]=nul;
- a[ZZ][XX]=a[ZZ][YY]=a[ZZ][ZZ]=nul;
-}
-
-static gmx_inline real iprod(const rvec a,const rvec b)
-{
- return (a[XX]*b[XX]+a[YY]*b[YY]+a[ZZ]*b[ZZ]);
-}
-
-static gmx_inline double diprod(const dvec a,const dvec b)
-{
- return (a[XX]*b[XX]+a[YY]*b[YY]+a[ZZ]*b[ZZ]);
-}
-
-static gmx_inline int iiprod(const ivec a,const ivec b)
-{
- return (a[XX]*b[XX]+a[YY]*b[YY]+a[ZZ]*b[ZZ]);
-}
-
-static gmx_inline real norm2(const rvec a)
-{
- return a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ];
-}
-
-static gmx_inline double dnorm2(const dvec a)
-{
- return a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ];
-}
-
-static gmx_inline real norm(const rvec a)
-{
- return (real)sqrt(a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ]);
-}
-
-static gmx_inline double dnorm(const dvec a)
-{
- return sqrt(a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ]);
-}
-
-/* WARNING:
- * Do _not_ use these routines to calculate the angle between two vectors
- * as acos(cos_angle(u,v)). While it might seem obvious, the acos function
- * is very flat close to -1 and 1, which will lead to accuracy-loss.
- * Instead, use the new gmx_angle() function directly.
- */
-static gmx_inline real
-cos_angle(const rvec a,const rvec b)
-{
- /*
- * ax*bx + ay*by + az*bz
- * cos-vec (a,b) = ---------------------
- * ||a|| * ||b||
- */
- real cosval;
- int m;
- double aa,bb,ip,ipa,ipb,ipab; /* For accuracy these must be double! */
-
- ip=ipa=ipb=0.0;
- for(m=0; (m<DIM); m++) { /* 18 */
- aa = a[m];
- bb = b[m];
- ip += aa*bb;
- ipa += aa*aa;
- ipb += bb*bb;
- }
- ipab = ipa*ipb;
- if (ipab > 0)
- cosval = ip*gmx_invsqrt(ipab); /* 7 */
- else
- cosval = 1;
- /* 25 TOTAL */
- if (cosval > 1.0)
- return 1.0;
- if (cosval <-1.0)
- return -1.0;
-
- return cosval;
-}
-
-/* WARNING:
- * Do _not_ use these routines to calculate the angle between two vectors
- * as acos(cos_angle(u,v)). While it might seem obvious, the acos function
- * is very flat close to -1 and 1, which will lead to accuracy-loss.
- * Instead, use the new gmx_angle() function directly.
- */
-static gmx_inline real
-cos_angle_no_table(const rvec a,const rvec b)
-{
- /* This version does not need the invsqrt lookup table */
- real cosval;
- int m;
- double aa,bb,ip,ipa,ipb; /* For accuracy these must be double! */
-
- ip=ipa=ipb=0.0;
- for(m=0; (m<DIM); m++) { /* 18 */
- aa = a[m];
- bb = b[m];
- ip += aa*bb;
- ipa += aa*aa;
- ipb += bb*bb;
- }
- cosval=ip/sqrt(ipa*ipb); /* 12 */
- /* 30 TOTAL */
- if (cosval > 1.0)
- return 1.0;
- if (cosval <-1.0)
- return -1.0;
-
- return cosval;
-}
-
-
-static gmx_inline void cprod(const rvec a,const rvec b,rvec c)
-{
- c[XX]=a[YY]*b[ZZ]-a[ZZ]*b[YY];
- c[YY]=a[ZZ]*b[XX]-a[XX]*b[ZZ];
- c[ZZ]=a[XX]*b[YY]-a[YY]*b[XX];
-}
-
-static gmx_inline void dcprod(const dvec a,const dvec b,dvec c)
-{
- c[XX]=a[YY]*b[ZZ]-a[ZZ]*b[YY];
- c[YY]=a[ZZ]*b[XX]-a[XX]*b[ZZ];
- c[ZZ]=a[XX]*b[YY]-a[YY]*b[XX];
-}
-
-/* This routine calculates the angle between a & b without any loss of accuracy close to 0/PI.
- * If you only need cos(theta), use the cos_angle() routines to save a few cycles.
- * This routine is faster than it might appear, since atan2 is accelerated on many CPUs (e.g. x86).
- */
-static gmx_inline real
-gmx_angle(const rvec a, const rvec b)
-{
- rvec w;
- real wlen,s;
-
- cprod(a,b,w);
-
- wlen = norm(w);
- s = iprod(a,b);
-
- return atan2(wlen,s);
-}
-
-static gmx_inline void mmul_ur0(matrix a,matrix b,matrix dest)
-{
- dest[XX][XX]=a[XX][XX]*b[XX][XX];
- dest[XX][YY]=0.0;
- dest[XX][ZZ]=0.0;
- dest[YY][XX]=a[YY][XX]*b[XX][XX]+a[YY][YY]*b[YY][XX];
- dest[YY][YY]= a[YY][YY]*b[YY][YY];
- dest[YY][ZZ]=0.0;
- dest[ZZ][XX]=a[ZZ][XX]*b[XX][XX]+a[ZZ][YY]*b[YY][XX]+a[ZZ][ZZ]*b[ZZ][XX];
- dest[ZZ][YY]= a[ZZ][YY]*b[YY][YY]+a[ZZ][ZZ]*b[ZZ][YY];
- dest[ZZ][ZZ]= a[ZZ][ZZ]*b[ZZ][ZZ];
-}
-
-static gmx_inline void mmul(matrix a,matrix b,matrix dest)
-{
- dest[XX][XX]=a[XX][XX]*b[XX][XX]+a[XX][YY]*b[YY][XX]+a[XX][ZZ]*b[ZZ][XX];
- dest[YY][XX]=a[YY][XX]*b[XX][XX]+a[YY][YY]*b[YY][XX]+a[YY][ZZ]*b[ZZ][XX];
- dest[ZZ][XX]=a[ZZ][XX]*b[XX][XX]+a[ZZ][YY]*b[YY][XX]+a[ZZ][ZZ]*b[ZZ][XX];
- dest[XX][YY]=a[XX][XX]*b[XX][YY]+a[XX][YY]*b[YY][YY]+a[XX][ZZ]*b[ZZ][YY];
- dest[YY][YY]=a[YY][XX]*b[XX][YY]+a[YY][YY]*b[YY][YY]+a[YY][ZZ]*b[ZZ][YY];
- dest[ZZ][YY]=a[ZZ][XX]*b[XX][YY]+a[ZZ][YY]*b[YY][YY]+a[ZZ][ZZ]*b[ZZ][YY];
- dest[XX][ZZ]=a[XX][XX]*b[XX][ZZ]+a[XX][YY]*b[YY][ZZ]+a[XX][ZZ]*b[ZZ][ZZ];
- dest[YY][ZZ]=a[YY][XX]*b[XX][ZZ]+a[YY][YY]*b[YY][ZZ]+a[YY][ZZ]*b[ZZ][ZZ];
- dest[ZZ][ZZ]=a[ZZ][XX]*b[XX][ZZ]+a[ZZ][YY]*b[YY][ZZ]+a[ZZ][ZZ]*b[ZZ][ZZ];
-}
-
-static gmx_inline void transpose(matrix src,matrix dest)
-{
- dest[XX][XX]=src[XX][XX];
- dest[YY][XX]=src[XX][YY];
- dest[ZZ][XX]=src[XX][ZZ];
- dest[XX][YY]=src[YY][XX];
- dest[YY][YY]=src[YY][YY];
- dest[ZZ][YY]=src[YY][ZZ];
- dest[XX][ZZ]=src[ZZ][XX];
- dest[YY][ZZ]=src[ZZ][YY];
- dest[ZZ][ZZ]=src[ZZ][ZZ];
-}
-
-static gmx_inline void tmmul(matrix a,matrix b,matrix dest)
-{
- /* Computes dest=mmul(transpose(a),b,dest) - used in do_pr_pcoupl */
- dest[XX][XX]=a[XX][XX]*b[XX][XX]+a[YY][XX]*b[YY][XX]+a[ZZ][XX]*b[ZZ][XX];
- dest[XX][YY]=a[XX][XX]*b[XX][YY]+a[YY][XX]*b[YY][YY]+a[ZZ][XX]*b[ZZ][YY];
- dest[XX][ZZ]=a[XX][XX]*b[XX][ZZ]+a[YY][XX]*b[YY][ZZ]+a[ZZ][XX]*b[ZZ][ZZ];
- dest[YY][XX]=a[XX][YY]*b[XX][XX]+a[YY][YY]*b[YY][XX]+a[ZZ][YY]*b[ZZ][XX];
- dest[YY][YY]=a[XX][YY]*b[XX][YY]+a[YY][YY]*b[YY][YY]+a[ZZ][YY]*b[ZZ][YY];
- dest[YY][ZZ]=a[XX][YY]*b[XX][ZZ]+a[YY][YY]*b[YY][ZZ]+a[ZZ][YY]*b[ZZ][ZZ];
- dest[ZZ][XX]=a[XX][ZZ]*b[XX][XX]+a[YY][ZZ]*b[YY][XX]+a[ZZ][ZZ]*b[ZZ][XX];
- dest[ZZ][YY]=a[XX][ZZ]*b[XX][YY]+a[YY][ZZ]*b[YY][YY]+a[ZZ][ZZ]*b[ZZ][YY];
- dest[ZZ][ZZ]=a[XX][ZZ]*b[XX][ZZ]+a[YY][ZZ]*b[YY][ZZ]+a[ZZ][ZZ]*b[ZZ][ZZ];
-}
-
-static gmx_inline void mtmul(matrix a,matrix b,matrix dest)
-{
- /* Computes dest=mmul(a,transpose(b),dest) - used in do_pr_pcoupl */
- dest[XX][XX]=a[XX][XX]*b[XX][XX]+a[XX][YY]*b[XX][YY]+a[XX][ZZ]*b[XX][ZZ];
- dest[XX][YY]=a[XX][XX]*b[YY][XX]+a[XX][YY]*b[YY][YY]+a[XX][ZZ]*b[YY][ZZ];
- dest[XX][ZZ]=a[XX][XX]*b[ZZ][XX]+a[XX][YY]*b[ZZ][YY]+a[XX][ZZ]*b[ZZ][ZZ];
- dest[YY][XX]=a[YY][XX]*b[XX][XX]+a[YY][YY]*b[XX][YY]+a[YY][ZZ]*b[XX][ZZ];
- dest[YY][YY]=a[YY][XX]*b[YY][XX]+a[YY][YY]*b[YY][YY]+a[YY][ZZ]*b[YY][ZZ];
- dest[YY][ZZ]=a[YY][XX]*b[ZZ][XX]+a[YY][YY]*b[ZZ][YY]+a[YY][ZZ]*b[ZZ][ZZ];
- dest[ZZ][XX]=a[ZZ][XX]*b[XX][XX]+a[ZZ][YY]*b[XX][YY]+a[ZZ][ZZ]*b[XX][ZZ];
- dest[ZZ][YY]=a[ZZ][XX]*b[YY][XX]+a[ZZ][YY]*b[YY][YY]+a[ZZ][ZZ]*b[YY][ZZ];
- dest[ZZ][ZZ]=a[ZZ][XX]*b[ZZ][XX]+a[ZZ][YY]*b[ZZ][YY]+a[ZZ][ZZ]*b[ZZ][ZZ];
-}
-
-static gmx_inline real det(matrix a)
-{
- return ( a[XX][XX]*(a[YY][YY]*a[ZZ][ZZ]-a[ZZ][YY]*a[YY][ZZ])
- -a[YY][XX]*(a[XX][YY]*a[ZZ][ZZ]-a[ZZ][YY]*a[XX][ZZ])
- +a[ZZ][XX]*(a[XX][YY]*a[YY][ZZ]-a[YY][YY]*a[XX][ZZ]));
-}
-
-static gmx_inline void m_add(matrix a,matrix b,matrix dest)
-{
- dest[XX][XX]=a[XX][XX]+b[XX][XX];
- dest[XX][YY]=a[XX][YY]+b[XX][YY];
- dest[XX][ZZ]=a[XX][ZZ]+b[XX][ZZ];
- dest[YY][XX]=a[YY][XX]+b[YY][XX];
- dest[YY][YY]=a[YY][YY]+b[YY][YY];
- dest[YY][ZZ]=a[YY][ZZ]+b[YY][ZZ];
- dest[ZZ][XX]=a[ZZ][XX]+b[ZZ][XX];
- dest[ZZ][YY]=a[ZZ][YY]+b[ZZ][YY];
- dest[ZZ][ZZ]=a[ZZ][ZZ]+b[ZZ][ZZ];
-}
-
-static gmx_inline void m_sub(matrix a,matrix b,matrix dest)
-{
- dest[XX][XX]=a[XX][XX]-b[XX][XX];
- dest[XX][YY]=a[XX][YY]-b[XX][YY];
- dest[XX][ZZ]=a[XX][ZZ]-b[XX][ZZ];
- dest[YY][XX]=a[YY][XX]-b[YY][XX];
- dest[YY][YY]=a[YY][YY]-b[YY][YY];
- dest[YY][ZZ]=a[YY][ZZ]-b[YY][ZZ];
- dest[ZZ][XX]=a[ZZ][XX]-b[ZZ][XX];
- dest[ZZ][YY]=a[ZZ][YY]-b[ZZ][YY];
- dest[ZZ][ZZ]=a[ZZ][ZZ]-b[ZZ][ZZ];
-}
-
-static gmx_inline void msmul(matrix m1,real r1,matrix dest)
-{
- dest[XX][XX]=r1*m1[XX][XX];
- dest[XX][YY]=r1*m1[XX][YY];
- dest[XX][ZZ]=r1*m1[XX][ZZ];
- dest[YY][XX]=r1*m1[YY][XX];
- dest[YY][YY]=r1*m1[YY][YY];
- dest[YY][ZZ]=r1*m1[YY][ZZ];
- dest[ZZ][XX]=r1*m1[ZZ][XX];
- dest[ZZ][YY]=r1*m1[ZZ][YY];
- dest[ZZ][ZZ]=r1*m1[ZZ][ZZ];
-}
-
-static gmx_inline void m_inv_ur0(matrix src,matrix dest)
-{
- double tmp = src[XX][XX]*src[YY][YY]*src[ZZ][ZZ];
- if (fabs(tmp) <= 100*GMX_REAL_MIN)
- gmx_fatal(FARGS,"Can not invert matrix, determinant is zero");
-
- dest[XX][XX] = 1/src[XX][XX];
- dest[YY][YY] = 1/src[YY][YY];
- dest[ZZ][ZZ] = 1/src[ZZ][ZZ];
- dest[ZZ][XX] = (src[YY][XX]*src[ZZ][YY]*dest[YY][YY]
- - src[ZZ][XX])*dest[XX][XX]*dest[ZZ][ZZ];
- dest[YY][XX] = -src[YY][XX]*dest[XX][XX]*dest[YY][YY];
- dest[ZZ][YY] = -src[ZZ][YY]*dest[YY][YY]*dest[ZZ][ZZ];
- dest[XX][YY] = 0.0;
- dest[XX][ZZ] = 0.0;
- dest[YY][ZZ] = 0.0;
-}
-
-static gmx_inline void m_inv(matrix src,matrix dest)
-{
- const real smallreal = (real)1.0e-24;
- const real largereal = (real)1.0e24;
- real deter,c,fc;
-
- deter = det(src);
- c = (real)1.0/deter;
- fc = (real)fabs(c);
-
- if ((fc <= smallreal) || (fc >= largereal))
- gmx_fatal(FARGS,"Can not invert matrix, determinant = %e",deter);
-
- dest[XX][XX]= c*(src[YY][YY]*src[ZZ][ZZ]-src[ZZ][YY]*src[YY][ZZ]);
- dest[XX][YY]=-c*(src[XX][YY]*src[ZZ][ZZ]-src[ZZ][YY]*src[XX][ZZ]);
- dest[XX][ZZ]= c*(src[XX][YY]*src[YY][ZZ]-src[YY][YY]*src[XX][ZZ]);
- dest[YY][XX]=-c*(src[YY][XX]*src[ZZ][ZZ]-src[ZZ][XX]*src[YY][ZZ]);
- dest[YY][YY]= c*(src[XX][XX]*src[ZZ][ZZ]-src[ZZ][XX]*src[XX][ZZ]);
- dest[YY][ZZ]=-c*(src[XX][XX]*src[YY][ZZ]-src[YY][XX]*src[XX][ZZ]);
- dest[ZZ][XX]= c*(src[YY][XX]*src[ZZ][YY]-src[ZZ][XX]*src[YY][YY]);
- dest[ZZ][YY]=-c*(src[XX][XX]*src[ZZ][YY]-src[ZZ][XX]*src[XX][YY]);
- dest[ZZ][ZZ]= c*(src[XX][XX]*src[YY][YY]-src[YY][XX]*src[XX][YY]);
-}
-
-static gmx_inline void mvmul(matrix a,const rvec src,rvec dest)
-{
- dest[XX]=a[XX][XX]*src[XX]+a[XX][YY]*src[YY]+a[XX][ZZ]*src[ZZ];
- dest[YY]=a[YY][XX]*src[XX]+a[YY][YY]*src[YY]+a[YY][ZZ]*src[ZZ];
- dest[ZZ]=a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
-}
-
-static gmx_inline void mvmul_ur0(matrix a,const rvec src,rvec dest)
-{
- dest[ZZ]=a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
- dest[YY]=a[YY][XX]*src[XX]+a[YY][YY]*src[YY];
- dest[XX]=a[XX][XX]*src[XX];
-}
-
-static gmx_inline void tmvmul_ur0(matrix a,const rvec src,rvec dest)
-{
- dest[XX]=a[XX][XX]*src[XX]+a[YY][XX]*src[YY]+a[ZZ][XX]*src[ZZ];
- dest[YY]= a[YY][YY]*src[YY]+a[ZZ][YY]*src[ZZ];
- dest[ZZ]= a[ZZ][ZZ]*src[ZZ];
-}
-
-static gmx_inline void unitv(const rvec src,rvec dest)
-{
- real linv;
-
- linv=gmx_invsqrt(norm2(src));
- dest[XX]=linv*src[XX];
- dest[YY]=linv*src[YY];
- dest[ZZ]=linv*src[ZZ];
-}
-
-static gmx_inline void unitv_no_table(const rvec src,rvec dest)
-{
- real linv;
-
- linv=1.0/sqrt(norm2(src));
- dest[XX]=linv*src[XX];
- dest[YY]=linv*src[YY];
- dest[ZZ]=linv*src[ZZ];
-}
-
-static void calc_lll(rvec box,rvec lll)
-{
- lll[XX] = 2.0*M_PI/box[XX];
- lll[YY] = 2.0*M_PI/box[YY];
- lll[ZZ] = 2.0*M_PI/box[ZZ];
-}
-
-static gmx_inline real trace(matrix m)
-{
- return (m[XX][XX]+m[YY][YY]+m[ZZ][ZZ]);
-}
-
-static gmx_inline real _divide(real a,real b,const char *file,int line)
-{
- if (fabs(b) <= GMX_REAL_MIN)
- gmx_fatal(FARGS,"Dividing by zero, file %s, line %d",file,line);
- return a/b;
-}
-
-static gmx_inline int _mod(int a,int b,char *file,int line)
-{
- if(b==0)
- gmx_fatal(FARGS,"Modulo zero, file %s, line %d",file,line);
- return a % b;
-}
-
-/* Operations on multidimensional rvecs, used e.g. in edsam.c */
-static void m_rveccopy(int dim, rvec *a, rvec *b)
-{
- /* b = a */
- int i;
-
- for (i=0; i<dim; i++)
- copy_rvec(a[i],b[i]);
-}
-
-/*computer matrix vectors from base vectors and angles */
-static void matrix_convert(matrix box, rvec vec, rvec angle)
-{
- svmul(DEG2RAD,angle,angle);
- box[XX][XX] = vec[XX];
- box[YY][XX] = vec[YY]*cos(angle[ZZ]);
- box[YY][YY] = vec[YY]*sin(angle[ZZ]);
- box[ZZ][XX] = vec[ZZ]*cos(angle[YY]);
- box[ZZ][YY] = vec[ZZ]
- *(cos(angle[XX])-cos(angle[YY])*cos(angle[ZZ]))/sin(angle[ZZ]);
- box[ZZ][ZZ] = sqrt(sqr(vec[ZZ])
- -box[ZZ][XX]*box[ZZ][XX]-box[ZZ][YY]*box[ZZ][YY]);
-}
-
-#define divide(a,b) _divide((a),(b),__FILE__,__LINE__)
-#define mod(a,b) _mod((a),(b),__FILE__,__LINE__)
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* _vec_h */
+++ /dev/null
-Makefile.in
-Makefile
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
--- /dev/null
+GMXRC
+GMXRC.*
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
--- /dev/null
+template
+gromacs
+Makefile.*-*
+++ /dev/null
-Makefile
-Makefile.in
�����،ߑ�߈��߆��ߜ��ߍ���ߐ�ߞ�ߚ���������������߸��������
�ߓ���ߋ�߈����ߋ���߶ߙ���ߓ���߶ߛ�ߌ�������������������
���߶ߗ���ߚ���������ߓ�����ߋ���ߚ���������ߚ�������߯������
+�����ߐ�ߜ�����������ߞ��ߋ��ߌ������߲�ߌ������ߖ�߈�������ߑ�����ߜ��������ײ�������
��،ߊ����ߜ���ߋ���ߐ���߆��ߞ��߶ߔ����״���߽����
��������ߘ����ߙ������ߓ����ߐ�ߓ�����ߐ��������ߺ���߯����
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
+++ /dev/null
-Makefile
-Makefile.in
-stamp-h.in
-stamp-h
-config.h
-config.h.in
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
-
-add_subdirectory(gmxlib)
-add_subdirectory(mdlib)
+add_subdirectory(gromacs)
add_subdirectory(kernel)
+add_subdirectory(programs)
if(NOT GMX_FAHCORE)
add_subdirectory(tools)
#pragma warning (disable : 4090)
#endif
+/* Source directory for accessing test data */
+#define SOURCE_DIR "@CMAKE_SOURCE_DIR@"
+
/* Name of package (translate from cmake to autoconf macro name) */
#define PACKAGE "@PROJECT_NAME@"
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
\ No newline at end of file
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
\ No newline at end of file
+++ /dev/null
-.deps
-.libs
-version.c
+++ /dev/null
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-
-
-# add target that generates version.c every time a make is run
-# only do this if we generate the version
-if(USE_VERSION_H)
- add_custom_target(gmx_version ALL
- COMMAND ${CMAKE_COMMAND}
- -D Git_EXECUTABLE="${Git_EXECUTABLE}"
- -D Git_VERSION="${Git_VERSION}"
- -D PROJECT_VERSION="${PROJECT_VERSION}"
- -D PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}"
- -D VERSION_C_CMAKEIN="${CMAKE_SOURCE_DIR}/src/gmxlib/version.c.cmakein"
- -D VERSION_C_OUT="${CMAKE_CURRENT_BINARY_DIR}/version.c"
- -P ${CMAKE_SOURCE_DIR}/cmake/gmxGenerateVersionInfo.cmake
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/gmxlib
- DEPENDS ${CMAKE_SOURCE_DIR}/src/gmxlib/version.c.cmakein
- COMMENT "Generating version information")
-endif()
-
-# The nonbonded directory contains subdirectories that are only
-# conditionally built, so we cannot use a GLOB_RECURSE here.
-file(GLOB GMXLIB_SOURCES *.c
- selection/*.c trajana/*.c
- statistics/*.c nonbonded/*.c nonbonded/nb_kernel_c/*.c)
-
-# This source file is generated
-file(GLOB VERSION_SOURCE version.c)
-
-if(VERSION_SOURCE)
- list(REMOVE_ITEM GMXLIB_SOURCES ${VERSION_SOURCE})
-endif(VERSION_SOURCE)
-
-# add version.c to the list of sources and tell cmake that it is generated
-if(USE_VERSION_H)
-LIST(APPEND GMXLIB_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/version.c) # auto-generated
-set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/version.c
- PROPERTIES GENERATED true)
-endif()
-
-if(GMX_DOUBLE)
- set(SSETYPE sse2)
-else()
- set(SSETYPE sse)
-endif()
-
-if(GMX_IA32_ASM)
- file(GLOB GMX_SSEKERNEL_C_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*.c)
- if(GMX_ASM_USEASM-NASM)
- file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*intel_syntax*.s)
- else()
- file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*${SSETYPE}.s nonbonded/nb_kernel_ia32_${SSETYPE}/*asm.s)
- endif()
-endif(GMX_IA32_ASM)
-
-if(GMX_X86_64_ASM)
- file(GLOB GMX_SSEKERNEL_C_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*.c)
- if(GMX_ASM_USEASM-NASM)
- file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*intel_syntax*.s)
- else()
- file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*${SSETYPE}.s nonbonded/nb_kernel_x86_64_${SSETYPE}/*asm.s)
- endif()
-endif(GMX_X86_64_ASM)
-
-if(GMX_FORTRAN)
- if (GMX_DOUBLE)
- file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_f77_double/*.[cf])
- else(GMX_DOUBLE)
- file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_f77_single/*.[cf])
- endif(GMX_DOUBLE)
-endif(GMX_FORTRAN)
-
-if(GMX_POWER6)
- file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_power6/*.[cF])
-endif(GMX_POWER6)
-
-if(GMX_BLUEGENE)
- file(GLOB GMX_BLUEGENE_C_SRC nonbonded/nb_kernel_bluegene/*.c)
-endif(GMX_BLUEGENE)
-
-if(GMX_PPC_ALTIVEC)
- file(GLOB GMX_PPC_ALTIVEC_SRC nonbonded/nb_kernel_ppc_altivec/*.c)
-endif(GMX_PPC_ALTIVEC)
-
-if(NOT GMX_EXTERNAL_BLAS)
- file(GLOB BLAS_SOURCES gmx_blas/*.c)
-endif(NOT GMX_EXTERNAL_BLAS)
-
-if(NOT GMX_EXTERNAL_LAPACK)
- file(GLOB LAPACK_SOURCES gmx_lapack/*.c)
-endif(NOT GMX_EXTERNAL_LAPACK)
-
-# This would be the standard way to include thread_mpi, but we want libgmx
-# to link the functions directly
-#if(GMX_THREADS)
-# add_subdirectory(thread_mpi)
-#endif(GMX_THREADS)
-#target_link_libraries(gmx ${GMX_EXTRA_LIBRARIES} ${THREAD_MPI_LIB})
-
-# Files called xxx_test.c are test drivers with a main() function for module xxx.c,
-# so they should not be included in the library
-file(GLOB_RECURSE NOT_GMXLIB_SOURCES *_test.c *\#*)
-list(REMOVE_ITEM GMXLIB_SOURCES ${NOT_GMXLIB_SOURCES})
-# Selection has test_ instead of _test. Removing here for special case, perhaps make general?
-if(GMX_FAHCORE)
- file(GLOB SELECTION_TEST selection/test*)
- list(REMOVE_ITEM GMXLIB_SOURCES ${SELECTION_TEST})
-endif(GMX_FAHCORE)
-
-# only fiddle with assembly kernels if we're not doing OpenMM build
-if(NOT GMX_OPENMM)
-if(GMX_ASM_USEASM-NASM)
- enable_language(ASM-NASM)
-
- # if NASM is used, we need a special build command for windows...
- FOREACH(SRC ${GMX_SSEKERNEL_ASM_SRC})
- GET_FILENAME_COMPONENT(FILE_BASE ${SRC} NAME_WE)
- SET(OBJ ${CMAKE_CURRENT_BINARY_DIR}/${FILE_BASE}${CMAKE_C_OUTPUT_EXTENSION})
-
- ADD_CUSTOM_COMMAND(OUTPUT ${OBJ}
- MAIN_DEPENDENCY ${SRC}
- COMMAND ${CMAKE_ASM-NASM_COMPILER} -f ${CMAKE_ASM-NASM_OBJECT_FORMAT} -o ${OBJ} ${SRC})
-
- SET(ALL_ASM_OBJS ${ALL_ASM_OBJS} ${OBJ})
- ENDFOREACH(SRC ${GMX_SSEKERNEL_ASM_SRC})
- set(GMX_SSEKERNEL_ASM_SRC ${ALL_ASM_OBJS})
-
-else(GMX_ASM_USEASM-NASM)
-
- enable_language(ASM-ATT)
- SET(CMAKE_ASM-ATT_COMPILER ${CMAKE_C_COMPILER})
- if(GMX_IA32_ASM)
- set_source_files_properties(${GMX_SSEKERNEL_ASM_SRC} PROPERTIES COMPILE_FLAGS "-c -m32")
- else()
- set_source_files_properties(${GMX_SSEKERNEL_ASM_SRC} PROPERTIES COMPILE_FLAGS "-c -m64")
- endif()
-
-endif(GMX_ASM_USEASM-NASM)
-endif(NOT GMX_OPENMM)
-
-add_library(gmx ${GMXLIB_SOURCES} ${BLAS_SOURCES} ${LAPACK_SOURCES} ${GMX_SSEKERNEL_C_SRC} ${GMX_SSEKERNEL_ASM_SRC} ${FORTRAN_SOURCES} ${GMX_BLUEGENE_C_SRC} ${GMX_PPC_ALTIVEC_SRC} ${THREAD_MPI_SRC})
-target_link_libraries(gmx ${GMX_EXTRA_LIBRARIES} ${THREAD_LIB})
-if(USE_VERSION_H)
- add_dependencies(gmx gmx_version)
-endif()
-set_target_properties(gmx PROPERTIES OUTPUT_NAME "gmx${GMX_LIBS_SUFFIX}" SOVERSION ${SOVERSION} INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
-
-install(TARGETS gmx DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)
-
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgmx.pc.cmakein ${CMAKE_CURRENT_BINARY_DIR}/libgmx.pc @ONLY)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgmx.pc
- DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
- RENAME "libgmx${GMX_LIBS_SUFFIX}.pc"
- COMPONENT development)
-
+++ /dev/null
-
-## Process this file with automake to produce Makefile.in
-# Note: Makefile is automatically generated from Makefile.in by the configure
-# script, and Makefile.in is generated from Makefile.am by automake.
-
-if !GMX_EXTERNAL_BLAS
- BLAS_DIR = gmx_blas
- BLAS_LIBOBJS = gmx_blas/libblas.la
-endif
-
-if !GMX_EXTERNAL_LAPACK
- LAPACK_DIR = gmx_lapack
- LAPACK_LIBOBJS = gmx_lapack/liblapack.la
-endif
-
-if THREAD_PARALLEL
- THREAD_MPI_LIBOBJS = thread_mpi/libthread_mpi.la
- THREAD_MPI_DIR = thread_mpi
-endif
-
-
-SUBDIRS = nonbonded selection statistics trajana $(THREAD_MPI_DIR) \
- $(BLAS_DIR) $(LAPACK_DIR)
-
-AM_CPPFLAGS= -I$(top_srcdir)/include -DGMXLIBDIR=\"$(datadir)/top\"
-
-lib_LTLIBRARIES = libgmx@LIBSUFFIX@.la
-
-#
-# Use a utility library for all the nonbonded kernels and wrapper routines.
-#
-libgmx@LIBSUFFIX@_la_LIBADD = nonbonded/libnonbonded.la \
- selection/libselection.la \
- statistics/libstatistics.la \
- trajana/libtrajana.la \
- $(THREAD_MPI_LIBOBJS) \
- $(BLAS_LIBOBJS) $(LAPACK_LIBOBJS)
-
-libgmx@LIBSUFFIX@_la_DEPENDENCIES = nonbonded/libnonbonded.la \
- selection/libselection.la \
- statistics/libstatistics.la \
- trajana/libtrajana.la \
- $(THREAD_MPI_LIBOBJS) \
- $(BLAS_LIBOBJS) $(LAPACK_LIBOBJS)
-
-#
-#
-libgmx@LIBSUFFIX@_la_LDFLAGS = -no-undefined -version-info @SHARED_VERSION_INFO@ @DLOPEN_LIBS@ $(PTHREAD_LIBS)
-
-libgmx@LIBSUFFIX@_la_SOURCES = \
- 3dview.c atomprop.c bondfree.c \
- calcgrid.c calch.c chargegroup.c checkpoint.c \
- confio.c copyrite.c disre.c do_fit.c \
- enxio.c ewald_util.c ffscanf.c \
- filenm.c futil.c gbutil.c gmx_fatal.c \
- gmx_sort.c gmxcpp.c \
- gmxfio.c ifunc.c index.c inputrec.c \
- cinvsqrtdata.c \
- invblock.c macros.c orires.c sparsematrix.c \
- main.c maths.c matio.c mshift.c \
- mtop_util.c mtxio.c mvdata.c names.c \
- network.c nrama.c nrjac.c nrnb.c \
- pargs.c pbc.c pdbio.c princ.c \
- rando.c random.c gmx_random.c rbin.c \
- readinp.c replace.c rmpbc.c shift_util.c \
- sortwater.c smalloc.c statutil.c sfactor.c \
- strdb.c string2.c symtab.c \
- topsort.c tpxio.c \
- trnio.c trxio.c txtdump.c typedefs.c \
- viewit.c warninp.c \
- wgms.c wman.c writeps.c \
- xdrd.c xtcio.c xvgr.c replace.h \
- libxdrf.c gmx_arpack.c gmx_matrix.c \
- dihres.c gmx_random_gausstable.h gmxfio_int.h\
- tcontrol.c splitter.c gmx_cyclecounter.c \
- gmx_system_xdr.c md5.c vmdio.c vmddlopen.c sighandler.c \
- oenv.c gmxfio_rw.c gmxfio_asc.c gmxfio_bin.c \
- gmxfio_xdr.c
-
-pkgconfigdir = ${libdir}/pkgconfig
-pkgconfig_DATA = libgmx@LIBSUFFIX@.pc
-
-# clean all libtool libraries, since the target names might have changed
-CLEANFILES = *.la *~ \\\#* innerc.c innerf.f mkinl
-
-if USE_VERSION_H
-#version.c contains generated git version information
-libgmx@LIBSUFFIX@_la_SOURCES += version.c
-CLEANFILES += version.c
-#The empty target FORCE forces make to run the command every time. But
-#version.c is only changed if the version actually has changed, and hence
-#rebuilds are only triggered when they are needed.
-version.c: FORCE
- $(top_srcdir)/src/gmxlib/genversion.sh @VERSION@ $(top_srcdir)
-FORCE:
-endif
-
-EXTRA_DIST = version.h libgmx.pc.cmakein
+++ /dev/null
-.libs
-.deps
+++ /dev/null
-# Convenience library for optional built-in BLAS routines - not installed.
-
-AM_CPPFLAGS= -I$(top_srcdir)/include
-
-
-noinst_LTLIBRARIES = libblas.la
-
-libblas_la_SOURCES = \
- dasum.c dcopy.c dgemm.c dger.c \
- drot.c dswap.c dsyr2.c dtrmm.c \
- dtrsm.c daxpy.c ddot.c dgemv.c \
- dnrm2.c dscal.c dsymv.c dsyr2k.c \
- dtrmv.c idamax.c \
- sasum.c scopy.c sgemm.c sger.c \
- srot.c sswap.c ssyr2.c strmm.c \
- strsm.c saxpy.c sdot.c sgemv.c \
- snrm2.c sscal.c ssymv.c ssyr2k.c \
- strmv.c isamax.c
-
-EXTRA_DIST = blas_copyright
-
-CLEANFILES = *.la *~ \\\#*
+++ /dev/null
-# Convenience library for optional built-in LAPACK routines - not installed.
-
-AM_CPPFLAGS= -I$(top_srcdir)/include
-
-
-noinst_LTLIBRARIES = liblapack.la
-
-liblapack_la_SOURCES = \
- dbdsdc.c dgetf2.c dlamrg.c dlarnv.c dlasd0.c dlasda.c dlasq6.c \
- dorgl2.c dormqr.c dbdsqr.c dgetrf.c dlange.c dlasd1.c dtrtri.c \
- dlasdq.c dlasr.c dorglq.c dormtr.c dgebd2.c dlabrd.c dlanst.c \
- dlasd2.c dlasdt.c dlasrt.c dorgqr.c dstebz.c dgebrd.c dsytrd.c \
- dlacpy.c dlapy2.c dlasd3.c dlaset.c dlassq.c dorm2l.c dgetrs.c \
- dstegr.c dgelq2.c dlae2.c dlasd4.c dlasq1.c dtrti2.c dgetri.c \
- dlasv2.c dorm2r.c dstein.c dgelqf.c dlaebz.c dlarf.c dlartg.c \
- dlasd5.c dlasq2.c dlaswp.c dormbr.c dsterf.c dgeqr2.c dlaed6.c \
- dlarfb.c dlaruv.c dlasd6.c dlasq3.c dlatrd.c dorml2.c dstevr.c \
- dgeqrf.c dlagtf.c dlarfg.c dlas2.c dlasd7.c dlasq4.c dorg2r.c \
- dormlq.c dsytd2.c dgesdd.c dlagts.c dlarft.c dlascl.c dlasd8.c \
- dlasq5.c dorgbr.c dormql.c dlaev2.c dsteqr.c dsyevr.c dlasrt2.c \
- dlansy.c \
- dlar1vx.c dlarrbx.c dlarrex.c dlarrfx.c \
- dlarrvx.c \
- sbdsdc.c sgetf2.c slamrg.c slarnv.c slasd0.c slasda.c slasq6.c \
- sorgl2.c sormqr.c sbdsqr.c sgetrf.c slange.c slasd1.c ssytrd.c \
- slasdq.c slasr.c sorglq.c sormtr.c sgebd2.c slabrd.c slanst.c \
- slasd2.c slasdt.c slasrt.c sorgqr.c sstebz.c sgebrd.c sgetrs.c \
- slacpy.c slapy2.c slasd3.c slaset.c slassq.c sorm2l.c sgetri.c \
- sstegr.c sgelq2.c slae2.c slasd4.c slasq1.c strti2.c strtri.c \
- slasv2.c sorm2r.c sstein.c sgelqf.c slaebz.c slarf.c slartg.c \
- slasd5.c slasq2.c slaswp.c sormbr.c ssterf.c sgeqr2.c slaed6.c \
- slarfb.c slaruv.c slasd6.c slasq3.c slatrd.c sorml2.c sstevr.c \
- sgeqrf.c slagtf.c slarfg.c slas2.c slasd7.c slasq4.c sorg2r.c \
- sormlq.c ssytd2.c sgesdd.c slagts.c slarft.c slascl.c slasd8.c \
- slasq5.c sorgbr.c sormql.c slaev2.c ssteqr.c ssyevr.c slasrt2.c \
- slansy.c \
- slar1vx.c slarrbx.c slarrex.c slarrfx.c \
- slarrvx.c \
- ilasrt2.c lapack_limits.h
-
-EXTRA_DIST = lapack_copyright
-
-CLEANFILES = *.la *~ \\\#*
+++ /dev/null
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCL_INSTALL_DIR@
-
-Name: libgmx
-Description: Gromacs default lib
-URL: http://www.gromacs.org
-Version: @PROJECT_VERSION@
-Requires:
-Libs.private: -lm @CMAKE_THREAD_LIBS_INIT@
-Libs: -L${libdir} -lgmx@LIBSUFFIX@
-Cflags: -I${includedir} @PKG_CFLAGS@
-
+++ /dev/null
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libgmx
-Description: Gromacs default lib
-URL: http://www.gromacs.org
-Version: @VERSION@
-Requires:
-Libs: -L${libdir} -lgmx@LIBSUFFIX@ @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ -lm
-Cflags: -I${includedir} @PTHREAD_CFLAGS@ @PKG_CFLAGS@
-
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
- */
-/* This file is completely threadsafe - keep it that way! */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sysstuff.h>
-#include <string.h>
-#include "typedefs.h"
-#include "main.h"
-#include "mvdata.h"
-#include "network.h"
-#include "smalloc.h"
-#include "gmx_fatal.h"
-#include "symtab.h"
-#include "vec.h"
-#include "tgroup.h"
-
-#define block_bc(cr, d) gmx_bcast( sizeof(d), &(d),(cr))
-/* Probably the test for (nr) > 0 in the next macro is only needed
- * on BlueGene(/L), where IBM's MPI_Bcast will segfault after
- * dereferencing a null pointer, even when no data is to be transferred. */
-#define nblock_bc(cr,nr,d) { if ((nr) > 0) gmx_bcast((nr)*sizeof((d)[0]), (d),(cr)); }
-#define snew_bc(cr,d,nr) { if (!MASTER(cr)) snew((d),(nr)); }
-/* Dirty macro with bAlloc not as an argument */
-#define nblock_abc(cr,nr,d) { if (bAlloc) snew((d),(nr)); nblock_bc(cr,(nr),(d)); }
-
-static void bc_string(const t_commrec *cr,t_symtab *symtab,char ***s)
-{
- int handle;
-
- if (MASTER(cr)) {
- handle = lookup_symtab(symtab,*s);
- }
- block_bc(cr,handle);
- if (!MASTER(cr)) {
- *s = get_symtab_handle(symtab,handle);
- }
-}
-
-static void bc_strings(const t_commrec *cr,t_symtab *symtab,int nr,char ****nm)
-{
- int i;
- int *handle;
- char ***NM;
-
- snew(handle,nr);
- if (MASTER(cr)) {
- NM = *nm;
- for(i=0; (i<nr); i++)
- handle[i] = lookup_symtab(symtab,NM[i]);
- }
- nblock_bc(cr,nr,handle);
-
- if (!MASTER(cr)) {
- snew_bc(cr,*nm,nr);
- NM = *nm;
- for (i=0; (i<nr); i++)
- (*nm)[i] = get_symtab_handle(symtab,handle[i]);
- }
- sfree(handle);
-}
-
-static void bc_strings_resinfo(const t_commrec *cr,t_symtab *symtab,
- int nr,t_resinfo *resinfo)
-{
- int i;
- int *handle;
-
- snew(handle,nr);
- if (MASTER(cr)) {
- for(i=0; (i<nr); i++)
- handle[i] = lookup_symtab(symtab,resinfo[i].name);
- }
- nblock_bc(cr,nr,handle);
-
- if (!MASTER(cr)) {
- for (i=0; (i<nr); i++)
- resinfo[i].name = get_symtab_handle(symtab,handle[i]);
- }
- sfree(handle);
-}
-
-static void bc_symtab(const t_commrec *cr,t_symtab *symtab)
-{
- int i,nr,len;
- t_symbuf *symbuf;
-
- block_bc(cr,symtab->nr);
- nr = symtab->nr;
- snew_bc(cr,symtab->symbuf,1);
- symbuf = symtab->symbuf;
- symbuf->bufsize = nr;
- snew_bc(cr,symbuf->buf,nr);
- for (i=0; i<nr; i++) {
- if (MASTER(cr))
- len = strlen(symbuf->buf[i]) + 1;
- block_bc(cr,len);
- snew_bc(cr,symbuf->buf[i],len);
- nblock_bc(cr,len,symbuf->buf[i]);
- }
-}
-
-static void bc_block(const t_commrec *cr,t_block *block)
-{
- block_bc(cr,block->nr);
- snew_bc(cr,block->index,block->nr+1);
- nblock_bc(cr,block->nr+1,block->index);
-}
-
-static void bc_blocka(const t_commrec *cr,t_blocka *block)
-{
- block_bc(cr,block->nr);
- snew_bc(cr,block->index,block->nr+1);
- nblock_bc(cr,block->nr+1,block->index);
- block_bc(cr,block->nra);
- if (block->nra) {
- snew_bc(cr,block->a,block->nra);
- nblock_bc(cr,block->nra,block->a);
- }
-}
-
-static void bc_grps(const t_commrec *cr,t_grps grps[])
-{
- int i;
-
- for(i=0; (i<egcNR); i++) {
- block_bc(cr,grps[i].nr);
- snew_bc(cr,grps[i].nm_ind,grps[i].nr);
- nblock_bc(cr,grps[i].nr,grps[i].nm_ind);
- }
-}
-
-static void bc_atoms(const t_commrec *cr,t_symtab *symtab,t_atoms *atoms)
-{
- int dummy;
-
- block_bc(cr,atoms->nr);
- snew_bc(cr,atoms->atom,atoms->nr);
- nblock_bc(cr,atoms->nr,atoms->atom);
- bc_strings(cr,symtab,atoms->nr,&atoms->atomname);
- block_bc(cr,atoms->nres);
- snew_bc(cr,atoms->resinfo,atoms->nres);
- nblock_bc(cr,atoms->nres,atoms->resinfo);
- bc_strings_resinfo(cr,symtab,atoms->nres,atoms->resinfo);
- /* QMMM requires atomtypes to be known on all nodes as well */
- bc_strings(cr,symtab,atoms->nr,&atoms->atomtype);
- bc_strings(cr,symtab,atoms->nr,&atoms->atomtypeB);
-}
-
-static void bc_groups(const t_commrec *cr,t_symtab *symtab,
- int natoms,gmx_groups_t *groups)
-{
- int dummy;
- int g,n;
-
- bc_grps(cr,groups->grps);
- block_bc(cr,groups->ngrpname);
- bc_strings(cr,symtab,groups->ngrpname,&groups->grpname);
- for(g=0; g<egcNR; g++) {
- if (MASTER(cr)) {
- if (groups->grpnr[g]) {
- n = natoms;
- } else {
- n = 0;
- }
- }
- block_bc(cr,n);
- if (n == 0) {
- groups->grpnr[g] = NULL;
- } else {
- snew_bc(cr,groups->grpnr[g],n);
- nblock_bc(cr,n,groups->grpnr[g]);
- }
- }
- if (debug) fprintf(debug,"after bc_groups\n");
-}
-
-void bcast_state_setup(const t_commrec *cr,t_state *state)
-{
- block_bc(cr,state->natoms);
- block_bc(cr,state->ngtc);
- block_bc(cr,state->nnhpres);
- block_bc(cr,state->nhchainlength);
- block_bc(cr,state->nrng);
- block_bc(cr,state->nrngi);
- block_bc(cr,state->flags);
-}
-
-void bcast_state(const t_commrec *cr,t_state *state,gmx_bool bAlloc)
-{
- int i,nnht,nnhtp;
-
- bcast_state_setup(cr,state);
-
- nnht = (state->ngtc)*(state->nhchainlength);
- nnhtp = (state->nnhpres)*(state->nhchainlength);
-
- if (MASTER(cr)) {
- bAlloc = FALSE;
- }
- if (bAlloc) {
- state->nalloc = state->natoms;
- }
- for(i=0; i<estNR; i++) {
- if (state->flags & (1<<i)) {
- switch (i) {
- case estLAMBDA: block_bc(cr,state->lambda); break;
- case estBOX: block_bc(cr,state->box); break;
- case estBOX_REL: block_bc(cr,state->box_rel); break;
- case estBOXV: block_bc(cr,state->boxv); break;
- case estPRES_PREV: block_bc(cr,state->pres_prev); break;
- case estSVIR_PREV: block_bc(cr,state->svir_prev); break;
- case estFVIR_PREV: block_bc(cr,state->fvir_prev); break;
- case estNH_XI: nblock_abc(cr,nnht,state->nosehoover_xi); break;
- case estNH_VXI: nblock_abc(cr,nnht,state->nosehoover_vxi); break;
- case estNHPRES_XI: nblock_abc(cr,nnhtp,state->nhpres_xi); break;
- case estNHPRES_VXI: nblock_abc(cr,nnhtp,state->nhpres_vxi); break;
- case estTC_INT: nblock_abc(cr,state->ngtc,state->therm_integral); break;
- case estVETA: block_bc(cr,state->veta); break;
- case estVOL0: block_bc(cr,state->vol0); break;
- case estX: nblock_abc(cr,state->natoms,state->x); break;
- case estV: nblock_abc(cr,state->natoms,state->v); break;
- case estSDX: nblock_abc(cr,state->natoms,state->sd_X); break;
- case estCGP: nblock_abc(cr,state->natoms,state->cg_p); break;
- case estLD_RNG: if(state->nrngi == 1) nblock_abc(cr,state->nrng,state->ld_rng); break;
- case estLD_RNGI: if(state->nrngi == 1) nblock_abc(cr,state->nrngi,state->ld_rngi); break;
- case estDISRE_INITF: block_bc(cr,state->hist.disre_initf); break;
- case estDISRE_RM3TAV:
- block_bc(cr,state->hist.ndisrepairs);
- nblock_abc(cr,state->hist.ndisrepairs,state->hist.disre_rm3tav);
- break;
- case estORIRE_INITF: block_bc(cr,state->hist.orire_initf); break;
- case estORIRE_DTAV:
- block_bc(cr,state->hist.norire_Dtav);
- nblock_abc(cr,state->hist.norire_Dtav,state->hist.orire_Dtav);
- break;
- default:
- gmx_fatal(FARGS,
- "Communication is not implemented for %s in bcast_state",
- est_names[i]);
- }
- }
- }
-}
-
-static void bc_ilists(const t_commrec *cr,t_ilist *ilist)
-{
- int ftype;
-
- /* Here we only communicate the non-zero length ilists */
- if (MASTER(cr)) {
- for(ftype=0; ftype<F_NRE; ftype++) {
- if (ilist[ftype].nr > 0) {
- block_bc(cr,ftype);
- block_bc(cr,ilist[ftype].nr);
- nblock_bc(cr,ilist[ftype].nr,ilist[ftype].iatoms);
- }
- }
- ftype = -1;
- block_bc(cr,ftype);
- } else {
- for(ftype=0; ftype<F_NRE; ftype++) {
- ilist[ftype].nr = 0;
- }
- do {
- block_bc(cr,ftype);
- if (ftype >= 0) {
- block_bc(cr,ilist[ftype].nr);
- snew_bc(cr,ilist[ftype].iatoms,ilist[ftype].nr);
- nblock_bc(cr,ilist[ftype].nr,ilist[ftype].iatoms);
- }
- } while (ftype >= 0);
- }
-
- if (debug) fprintf(debug,"after bc_ilists\n");
-}
-
-static void bc_idef(const t_commrec *cr,t_idef *idef)
-{
- block_bc(cr,idef->ntypes);
- block_bc(cr,idef->atnr);
- snew_bc(cr,idef->functype,idef->ntypes);
- snew_bc(cr,idef->iparams,idef->ntypes);
- nblock_bc(cr,idef->ntypes,idef->functype);
- nblock_bc(cr,idef->ntypes,idef->iparams);
- block_bc(cr,idef->fudgeQQ);
- bc_ilists(cr,idef->il);
- block_bc(cr,idef->ilsort);
-}
-
-static void bc_cmap(const t_commrec *cr, gmx_cmap_t *cmap_grid)
-{
- int i,j,nelem,ngrid;
-
- block_bc(cr,cmap_grid->ngrid);
- block_bc(cr,cmap_grid->grid_spacing);
-
- ngrid = cmap_grid->ngrid;
- nelem = cmap_grid->grid_spacing * cmap_grid->grid_spacing;
-
- if(ngrid>0)
- {
- snew_bc(cr,cmap_grid->cmapdata,ngrid);
-
- for(i=0;i<ngrid;i++)
- {
- snew_bc(cr,cmap_grid->cmapdata[i].cmap,4*nelem);
- nblock_bc(cr,4*nelem,cmap_grid->cmapdata[i].cmap);
- }
- }
-}
-
-static void bc_ffparams(const t_commrec *cr,gmx_ffparams_t *ffp)
-{
- int i;
-
- block_bc(cr,ffp->ntypes);
- block_bc(cr,ffp->atnr);
- snew_bc(cr,ffp->functype,ffp->ntypes);
- snew_bc(cr,ffp->iparams,ffp->ntypes);
- nblock_bc(cr,ffp->ntypes,ffp->functype);
- nblock_bc(cr,ffp->ntypes,ffp->iparams);
- block_bc(cr,ffp->reppow);
- block_bc(cr,ffp->fudgeQQ);
- bc_cmap(cr,&ffp->cmap_grid);
-}
-
-static void bc_grpopts(const t_commrec *cr,t_grpopts *g)
-{
- int i,n;
-
- block_bc(cr,g->ngtc);
- block_bc(cr,g->ngacc);
- block_bc(cr,g->ngfrz);
- block_bc(cr,g->ngener);
- snew_bc(cr,g->nrdf,g->ngtc);
- snew_bc(cr,g->tau_t,g->ngtc);
- snew_bc(cr,g->ref_t,g->ngtc);
- snew_bc(cr,g->acc,g->ngacc);
- snew_bc(cr,g->nFreeze,g->ngfrz);
- snew_bc(cr,g->egp_flags,g->ngener*g->ngener);
-
- nblock_bc(cr,g->ngtc,g->nrdf);
- nblock_bc(cr,g->ngtc,g->tau_t);
- nblock_bc(cr,g->ngtc,g->ref_t);
- nblock_bc(cr,g->ngacc,g->acc);
- nblock_bc(cr,g->ngfrz,g->nFreeze);
- nblock_bc(cr,g->ngener*g->ngener,g->egp_flags);
- snew_bc(cr,g->annealing,g->ngtc);
- snew_bc(cr,g->anneal_npoints,g->ngtc);
- snew_bc(cr,g->anneal_time,g->ngtc);
- snew_bc(cr,g->anneal_temp,g->ngtc);
- nblock_bc(cr,g->ngtc,g->annealing);
- nblock_bc(cr,g->ngtc,g->anneal_npoints);
- for(i=0;(i<g->ngtc); i++) {
- n = g->anneal_npoints[i];
- if (n > 0) {
- snew_bc(cr,g->anneal_time[i],n);
- snew_bc(cr,g->anneal_temp[i],n);
- nblock_bc(cr,n,g->anneal_time[i]);
- nblock_bc(cr,n,g->anneal_temp[i]);
- }
- }
-
- /* QMMM stuff, see inputrec */
- block_bc(cr,g->ngQM);
- snew_bc(cr,g->QMmethod,g->ngQM);
- snew_bc(cr,g->QMbasis,g->ngQM);
- snew_bc(cr,g->QMcharge,g->ngQM);
- snew_bc(cr,g->QMmult,g->ngQM);
- snew_bc(cr,g->bSH,g->ngQM);
- snew_bc(cr,g->CASorbitals,g->ngQM);
- snew_bc(cr,g->CASelectrons,g->ngQM);
- snew_bc(cr,g->SAon,g->ngQM);
- snew_bc(cr,g->SAoff,g->ngQM);
- snew_bc(cr,g->SAsteps,g->ngQM);
-
- if (g->ngQM)
- {
- nblock_bc(cr,g->ngQM,g->QMmethod);
- nblock_bc(cr,g->ngQM,g->QMbasis);
- nblock_bc(cr,g->ngQM,g->QMcharge);
- nblock_bc(cr,g->ngQM,g->QMmult);
- nblock_bc(cr,g->ngQM,g->bSH);
- nblock_bc(cr,g->ngQM,g->CASorbitals);
- nblock_bc(cr,g->ngQM,g->CASelectrons);
- nblock_bc(cr,g->ngQM,g->SAon);
- nblock_bc(cr,g->ngQM,g->SAoff);
- nblock_bc(cr,g->ngQM,g->SAsteps);
- /* end of QMMM stuff */
- }
-}
-
-static void bc_cosines(const t_commrec *cr,t_cosines *cs)
-{
- block_bc(cr,cs->n);
- snew_bc(cr,cs->a,cs->n);
- snew_bc(cr,cs->phi,cs->n);
- if (cs->n > 0) {
- nblock_bc(cr,cs->n,cs->a);
- nblock_bc(cr,cs->n,cs->phi);
- }
-}
-
-static void bc_pullgrp(const t_commrec *cr,t_pullgrp *pgrp)
-{
- block_bc(cr,*pgrp);
- if (pgrp->nat > 0) {
- snew_bc(cr,pgrp->ind,pgrp->nat);
- nblock_bc(cr,pgrp->nat,pgrp->ind);
- }
- if (pgrp->nweight > 0) {
- snew_bc(cr,pgrp->weight,pgrp->nweight);
- nblock_bc(cr,pgrp->nweight,pgrp->weight);
- }
-}
-
-static void bc_pull(const t_commrec *cr,t_pull *pull)
-{
- int g;
-
- block_bc(cr,*pull);
- snew_bc(cr,pull->grp,pull->ngrp+1);
- for(g=0; g<pull->ngrp+1; g++)
- {
- bc_pullgrp(cr,&pull->grp[g]);
- }
-}
-
-static void bc_inputrec(const t_commrec *cr,t_inputrec *inputrec)
-{
- gmx_bool bAlloc=TRUE;
- int i;
-
- block_bc(cr,*inputrec);
- snew_bc(cr,inputrec->flambda,inputrec->n_flambda);
- nblock_bc(cr,inputrec->n_flambda,inputrec->flambda);
- bc_grpopts(cr,&(inputrec->opts));
- if (inputrec->ePull != epullNO) {
- snew_bc(cr,inputrec->pull,1);
- bc_pull(cr,inputrec->pull);
- }
- for(i=0; (i<DIM); i++) {
- bc_cosines(cr,&(inputrec->ex[i]));
- bc_cosines(cr,&(inputrec->et[i]));
- }
-}
-
-static void bc_moltype(const t_commrec *cr,t_symtab *symtab,
- gmx_moltype_t *moltype)
-{
- bc_string(cr,symtab,&moltype->name);
- bc_atoms(cr,symtab,&moltype->atoms);
- if (debug) fprintf(debug,"after bc_atoms\n");
-
- bc_ilists(cr,moltype->ilist);
- bc_block(cr,&moltype->cgs);
- bc_blocka(cr,&moltype->excls);
-}
-
-static void bc_molblock(const t_commrec *cr,gmx_molblock_t *molb)
-{
- gmx_bool bAlloc=TRUE;
-
- block_bc(cr,molb->type);
- block_bc(cr,molb->nmol);
- block_bc(cr,molb->natoms_mol);
- block_bc(cr,molb->nposres_xA);
- if (molb->nposres_xA > 0) {
- snew_bc(cr,molb->posres_xA,molb->nposres_xA);
- nblock_bc(cr,molb->nposres_xA*DIM,molb->posres_xA[0]);
- }
- block_bc(cr,molb->nposres_xB);
- if (molb->nposres_xB > 0) {
- snew_bc(cr,molb->posres_xB,molb->nposres_xB);
- nblock_bc(cr,molb->nposres_xB*DIM,molb->posres_xB[0]);
- }
- if (debug) fprintf(debug,"after bc_molblock\n");
-}
-
-static void bc_atomtypes(const t_commrec *cr, t_atomtypes *atomtypes)
-{
- int nr;
-
- block_bc(cr,atomtypes->nr);
-
- nr = atomtypes->nr;
-
- snew_bc(cr,atomtypes->radius,nr);
- snew_bc(cr,atomtypes->vol,nr);
- snew_bc(cr,atomtypes->surftens,nr);
- snew_bc(cr,atomtypes->gb_radius,nr);
- snew_bc(cr,atomtypes->S_hct,nr);
-
- nblock_bc(cr,nr,atomtypes->radius);
- nblock_bc(cr,nr,atomtypes->vol);
- nblock_bc(cr,nr,atomtypes->surftens);
- nblock_bc(cr,nr,atomtypes->gb_radius);
- nblock_bc(cr,nr,atomtypes->S_hct);
-}
-
-
-void bcast_ir_mtop(const t_commrec *cr,t_inputrec *inputrec,gmx_mtop_t *mtop)
-{
- int i;
- if (debug) fprintf(debug,"in bc_data\n");
- bc_inputrec(cr,inputrec);
- if (debug) fprintf(debug,"after bc_inputrec\n");
- bc_symtab(cr,&mtop->symtab);
- if (debug) fprintf(debug,"after bc_symtab\n");
- bc_string(cr,&mtop->symtab,&mtop->name);
- if (debug) fprintf(debug,"after bc_name\n");
-
- bc_ffparams(cr,&mtop->ffparams);
-
- block_bc(cr,mtop->nmoltype);
- snew_bc(cr,mtop->moltype,mtop->nmoltype);
- for(i=0; i<mtop->nmoltype; i++) {
- bc_moltype(cr,&mtop->symtab,&mtop->moltype[i]);
- }
-
- block_bc(cr,mtop->nmolblock);
- snew_bc(cr,mtop->molblock,mtop->nmolblock);
- for(i=0; i<mtop->nmolblock; i++) {
- bc_molblock(cr,&mtop->molblock[i]);
- }
-
- block_bc(cr,mtop->natoms);
-
- bc_atomtypes(cr,&mtop->atomtypes);
-
- bc_block(cr,&mtop->mols);
- bc_groups(cr,&mtop->symtab,mtop->natoms,&mtop->groups);
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "typedefs.h"
-#include "names.h"
-
-/* note: these arrays should correspond to enums in include/types/enums.h */
-
-const char *epbc_names[epbcNR+1]=
-{
- "xyz", "no", "xy", "screw", NULL
-};
-
-const char *ens_names[ensNR+1]=
-{
- "Grid","Simple", NULL
-};
-
-const char *ei_names[eiNR+1]=
-{
- "md", "steep", "cg", "bd", "sd", "nm", "l-bfgs", "tpi", "tpic", "sd1", "md-vv", "md-vv-avek",NULL
-};
-
-const char *bool_names[BOOL_NR+1]=
-{
- "FALSE","TRUE", NULL
-};
-
-const char *yesno_names[BOOL_NR+1]=
-{
- "no","yes", NULL
-};
-
-const char *ptype_str[eptNR+1] = {
- "Atom", "Nucleus", "Shell", "Bond", "VSite", NULL
-};
-
-const char *eel_names[eelNR+1] = {
- "Cut-off", "Reaction-Field", "Generalized-Reaction-Field",
- "PME", "Ewald", "PPPM", "Poisson", "Switch", "Shift", "User",
- "Generalized-Born", "Reaction-Field-nec", "Encad-shift",
- "PME-User", "PME-Switch", "PME-User-Switch",
- "Reaction-Field-zero", NULL
-};
-
-const char *eewg_names[eewgNR+1] = {
- "3d", "3dc", NULL
-};
-
-const char *evdw_names[evdwNR+1] = {
- "Cut-off", "Switch", "Shift", "User", "Encad-shift", NULL
-};
-
-const char *econstr_names[econtNR+1] = {
- "Lincs", "Shake", NULL
-};
-
-const char *egrp_nm[egNR+1] = {
- "Coul-SR","LJ-SR","Buck-SR", "Coul-LR", "LJ-LR", "Buck-LR",
- "Coul-14", "LJ-14", NULL
-};
-
-const char *etcoupl_names[etcNR+1] = {
- "No", "Berendsen", "Nose-Hoover", "yes", "Andersen", "Andersen-interval", "V-rescale", NULL
-}; /* yes is alias for berendsen */
-
-const char *epcoupl_names[epcNR+1] = {
- "No", "Berendsen", "Parrinello-Rahman", "Isotropic", "MTTK", NULL
-}; /* isotropic is alias for berendsen */
-
-const char *epcoupltype_names[epctNR+1] = {
- "Isotropic", "Semiisotropic", "Anisotropic", "Surface-Tension", NULL
-};
-
-const char *erefscaling_names[erscNR+1] = {
- "No", "All", "COM", NULL
-};
-
-const char *edisre_names[edrNR+1] = {
- "No", "Simple", "Ensemble", NULL
-};
-
-const char *edisreweighting_names[edrwNR+1] = {
- "Conservative", "Equal", NULL
-};
-
-const char *enbf_names[eNBF_NR+1] = {
- "", "LJ", "Buckingham", NULL
-};
-
-const char *ecomb_names[eCOMB_NR+1] = {
- "", "Geometric", "Arithmetic", "GeomSigEps", NULL
-};
-
-const char *gtypes[egcNR+1] = {
- "T-Coupling", "Energy Mon.", "Acceleration", "Freeze",
- "User1", "User2", "VCM", "XTC", "Or. Res. Fit", "QMMM", NULL
-};
-
-const char *efep_names[efepNR+1] = {
- "no", "yes", NULL
-};
-
-const char *separate_dhdl_file_names[sepdhdlfileNR+1] = {
- "yes", "no", NULL
-};
-
-const char *dhdl_derivatives_names[dhdlderivativesNR+1] = {
- "yes", "no", NULL
-};
-
-const char *esol_names[esolNR+1] = {
- "No", "SPC", "TIP4p", NULL
-};
-
-const char *enlist_names[enlistNR+1] = {
- "Atom-Atom", "SPC-Atom", "SPC-SPC", "TIP4p-Atom", "TIP4p-TIP4p", "CG-CG", NULL
-};
-
-const char *edispc_names[edispcNR+1] = {
- "No", "EnerPres", "Ener", "AllEnerPres", "AllEner", NULL
-};
-
-const char *ecm_names[ecmNR+1] = {
- "Linear", "Angular", "None", NULL
-};
-
-const char *eann_names[eannNR+1] = {
- "No", "Single", "Periodic", NULL
-};
-
-const char *eis_names[eisNR+1] = {
- "No", "GBSA", NULL
-};
-
-const char *egb_names[egbNR+1] = {
- "Still", "HCT", "OBC", NULL
-};
-
-const char *esa_names[esaNR+1] = {
- "Ace-approximation", "None", "Still", NULL
-};
-
-const char *ewt_names[ewtNR+1] = {
- "9-3", "10-4", "table", "12-6", NULL
-};
-
-const char *epull_names[epullNR+1] = {
- "no", "umbrella", "constraint", "constant_force", NULL
-};
-
-const char *epullg_names[epullgNR+1] = {
- "distance", "direction", "cylinder", "position", "direction_periodic", NULL
-};
-
-const char *eQMmethod_names[eQMmethodNR+1] = {
- "AM1", "PM3", "RHF",
- "UHF", "DFT", "B3LYP", "MP2", "CASSCF","B3LYPLAN",
- "DIRECT", NULL
-};
-
-const char *eQMbasis_names[eQMbasisNR+1] = {
- "STO3G", "STO-3G", "3-21G",
- "3-21G*", "3-21+G*", "6-21G",
- "6-31G", "6-31G*", "6-31+G*",
- "6-311G", NULL
-};
-
-const char *eQMMMscheme_names[eQMMMschemeNR+1] = {
- "normal", "ONIOM", NULL
-};
-
-const char *eMultentOpt_names[eMultentOptNR+1] = {
- "multiple_entries", "no", "use_last", NULL
-};
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include "gmx_fatal.h"
-#include "main.h"
-#include "smalloc.h"
-#include "network.h"
-#include "copyrite.h"
-#include "statutil.h"
-#include "ctype.h"
-#include "macros.h"
-
-#ifdef GMX_LIB_MPI
-#include <mpi.h>
-#endif
-
-#ifdef GMX_THREADS
-#include "tmpi.h"
-#endif
-
-#include "mpelogging.h"
-
-/* The source code in this file should be thread-safe.
- Please keep it that way. */
-
-gmx_bool gmx_mpi_initialized(void)
-{
- int n;
-#ifndef GMX_MPI
- return 0;
-#else
- MPI_Initialized(&n);
-
- return n;
-#endif
-}
-
-int gmx_setup(int *argc,char **argv,int *nnodes)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_setup");
- return 0;
-#else
- char buf[256];
- int resultlen; /* actual length of node name */
- int i,flag;
- int mpi_num_nodes;
- int mpi_my_rank;
- char mpi_hostname[MPI_MAX_PROCESSOR_NAME];
-
- /* Call the MPI routines */
-#ifdef GMX_LIB_MPI
-#ifdef GMX_FAHCORE
- (void) fah_MPI_Init(argc,&argv);
-#else
- (void) MPI_Init(argc,&argv);
-#endif
-#endif
- (void) MPI_Comm_size( MPI_COMM_WORLD, &mpi_num_nodes );
- (void) MPI_Comm_rank( MPI_COMM_WORLD, &mpi_my_rank );
- (void) MPI_Get_processor_name( mpi_hostname, &resultlen );
-
-
-#ifdef USE_MPE
- /* MPE logging routines. Get event IDs from MPE: */
- /* General events */
- ev_timestep1 = MPE_Log_get_event_number( );
- ev_timestep2 = MPE_Log_get_event_number( );
- ev_force_start = MPE_Log_get_event_number( );
- ev_force_finish = MPE_Log_get_event_number( );
- ev_do_fnbf_start = MPE_Log_get_event_number( );
- ev_do_fnbf_finish = MPE_Log_get_event_number( );
- ev_ns_start = MPE_Log_get_event_number( );
- ev_ns_finish = MPE_Log_get_event_number( );
- ev_calc_bonds_start = MPE_Log_get_event_number( );
- ev_calc_bonds_finish = MPE_Log_get_event_number( );
- ev_global_stat_start = MPE_Log_get_event_number( );
- ev_global_stat_finish = MPE_Log_get_event_number( );
- ev_virial_start = MPE_Log_get_event_number( );
- ev_virial_finish = MPE_Log_get_event_number( );
-
- /* Shift related events */
- ev_shift_start = MPE_Log_get_event_number( );
- ev_shift_finish = MPE_Log_get_event_number( );
- ev_unshift_start = MPE_Log_get_event_number( );
- ev_unshift_finish = MPE_Log_get_event_number( );
- ev_mk_mshift_start = MPE_Log_get_event_number( );
- ev_mk_mshift_finish = MPE_Log_get_event_number( );
-
- /* PME related events */
- ev_pme_start = MPE_Log_get_event_number( );
- ev_pme_finish = MPE_Log_get_event_number( );
- ev_spread_on_grid_start = MPE_Log_get_event_number( );
- ev_spread_on_grid_finish = MPE_Log_get_event_number( );
- ev_sum_qgrid_start = MPE_Log_get_event_number( );
- ev_sum_qgrid_finish = MPE_Log_get_event_number( );
- ev_gmxfft3d_start = MPE_Log_get_event_number( );
- ev_gmxfft3d_finish = MPE_Log_get_event_number( );
- ev_solve_pme_start = MPE_Log_get_event_number( );
- ev_solve_pme_finish = MPE_Log_get_event_number( );
- ev_gather_f_bsplines_start = MPE_Log_get_event_number( );
- ev_gather_f_bsplines_finish= MPE_Log_get_event_number( );
- ev_reduce_start = MPE_Log_get_event_number( );
- ev_reduce_finish = MPE_Log_get_event_number( );
- ev_rscatter_start = MPE_Log_get_event_number( );
- ev_rscatter_finish = MPE_Log_get_event_number( );
- ev_alltoall_start = MPE_Log_get_event_number( );
- ev_alltoall_finish = MPE_Log_get_event_number( );
- ev_pmeredist_start = MPE_Log_get_event_number( );
- ev_pmeredist_finish = MPE_Log_get_event_number( );
- ev_init_pme_start = MPE_Log_get_event_number( );
- ev_init_pme_finish = MPE_Log_get_event_number( );
- ev_send_coordinates_start = MPE_Log_get_event_number( );
- ev_send_coordinates_finish = MPE_Log_get_event_number( );
- ev_update_fr_start = MPE_Log_get_event_number( );
- ev_update_fr_finish = MPE_Log_get_event_number( );
- ev_clear_rvecs_start = MPE_Log_get_event_number( );
- ev_clear_rvecs_finish = MPE_Log_get_event_number( );
- ev_update_start = MPE_Log_get_event_number( );
- ev_update_finish = MPE_Log_get_event_number( );
- ev_output_start = MPE_Log_get_event_number( );
- ev_output_finish = MPE_Log_get_event_number( );
- ev_sum_lrforces_start = MPE_Log_get_event_number( );
- ev_sum_lrforces_finish = MPE_Log_get_event_number( );
- ev_sort_start = MPE_Log_get_event_number( );
- ev_sort_finish = MPE_Log_get_event_number( );
- ev_sum_qgrid_start = MPE_Log_get_event_number( );
- ev_sum_qgrid_finish = MPE_Log_get_event_number( );
-
- /* Essential dynamics related events */
- ev_edsam_start = MPE_Log_get_event_number( );
- ev_edsam_finish = MPE_Log_get_event_number( );
- ev_get_coords_start = MPE_Log_get_event_number( );
- ev_get_coords_finish = MPE_Log_get_event_number( );
- ev_ed_apply_cons_start = MPE_Log_get_event_number( );
- ev_ed_apply_cons_finish = MPE_Log_get_event_number( );
- ev_fit_to_reference_start = MPE_Log_get_event_number( );
- ev_fit_to_reference_finish = MPE_Log_get_event_number( );
-
- /* describe events: */
- if ( mpi_my_rank == 0 )
- {
- /* General events */
- MPE_Describe_state(ev_timestep1, ev_timestep2, "timestep START", "magenta" );
- MPE_Describe_state(ev_force_start, ev_force_finish, "force", "cornflower blue" );
- MPE_Describe_state(ev_do_fnbf_start, ev_do_fnbf_finish, "do_fnbf", "navy" );
- MPE_Describe_state(ev_ns_start, ev_ns_finish, "neighbor search", "tomato" );
- MPE_Describe_state(ev_calc_bonds_start, ev_calc_bonds_finish, "bonded forces", "slate blue" );
- MPE_Describe_state(ev_global_stat_start, ev_global_stat_finish, "global stat", "firebrick3");
- MPE_Describe_state(ev_update_fr_start, ev_update_fr_finish, "update forcerec", "goldenrod");
- MPE_Describe_state(ev_clear_rvecs_start, ev_clear_rvecs_finish, "clear rvecs", "bisque");
- MPE_Describe_state(ev_update_start, ev_update_finish, "update", "cornsilk");
- MPE_Describe_state(ev_output_start, ev_output_finish, "output", "black");
- MPE_Describe_state(ev_virial_start, ev_virial_finish, "calc_virial", "thistle4");
-
- /* PME related events */
- MPE_Describe_state(ev_pme_start, ev_pme_finish, "doing PME", "grey" );
- MPE_Describe_state(ev_spread_on_grid_start, ev_spread_on_grid_finish, "spread", "dark orange" );
- MPE_Describe_state(ev_sum_qgrid_start, ev_sum_qgrid_finish, "sum qgrid", "slate blue");
- MPE_Describe_state(ev_gmxfft3d_start, ev_gmxfft3d_finish, "fft3d", "snow2" );
- MPE_Describe_state(ev_solve_pme_start, ev_solve_pme_finish, "solve PME", "indian red" );
- MPE_Describe_state(ev_gather_f_bsplines_start, ev_gather_f_bsplines_finish, "bsplines", "light sea green" );
- MPE_Describe_state(ev_reduce_start, ev_reduce_finish, "reduce", "cyan1" );
- MPE_Describe_state(ev_rscatter_start, ev_rscatter_finish, "rscatter", "cyan3" );
- MPE_Describe_state(ev_alltoall_start, ev_alltoall_finish, "alltoall", "LightCyan4" );
- MPE_Describe_state(ev_pmeredist_start, ev_pmeredist_finish, "pmeredist", "thistle" );
- MPE_Describe_state(ev_init_pme_start, ev_init_pme_finish, "init PME", "snow4");
- MPE_Describe_state(ev_send_coordinates_start, ev_send_coordinates_finish, "send_coordinates","blue");
- MPE_Describe_state(ev_sum_lrforces_start, ev_sum_lrforces_finish, "sum_LRforces", "lime green");
- MPE_Describe_state(ev_sort_start, ev_sort_finish, "sort pme atoms", "brown");
- MPE_Describe_state(ev_sum_qgrid_start, ev_sum_qgrid_finish, "sum charge grid", "medium orchid");
-
- /* Shift related events */
- MPE_Describe_state(ev_shift_start, ev_shift_finish, "shift", "orange");
- MPE_Describe_state(ev_unshift_start, ev_unshift_finish, "unshift", "dark orange");
- MPE_Describe_state(ev_mk_mshift_start, ev_mk_mshift_finish, "mk_mshift", "maroon");
-
- /* Essential dynamics related events */
- MPE_Describe_state(ev_edsam_start, ev_edsam_finish, "EDSAM", "deep sky blue");
- MPE_Describe_state(ev_get_coords_start, ev_get_coords_finish, "ED get coords", "steel blue");
- MPE_Describe_state(ev_ed_apply_cons_start, ev_ed_apply_cons_finish, "ED apply constr", "forest green");
- MPE_Describe_state(ev_fit_to_reference_start, ev_fit_to_reference_finish, "ED fit to ref", "lavender");
-
- }
- MPE_Init_log();
-#endif
-
-#ifdef GMX_LIB_MPI
- fprintf(stderr,"NNODES=%d, MYRANK=%d, HOSTNAME=%s\n",
- mpi_num_nodes,mpi_my_rank,mpi_hostname);
-#endif
-
- *nnodes=mpi_num_nodes;
-
- return mpi_my_rank;
-#endif
-}
-
-int gmx_node_num(void)
-{
-#ifndef GMX_MPI
- return 1;
-#else
- int i;
- (void) MPI_Comm_size(MPI_COMM_WORLD, &i);
- return i;
-#endif
-}
-
-int gmx_node_rank(void)
-{
-#ifndef GMX_MPI
- return 0;
-#else
- int i;
- (void) MPI_Comm_rank(MPI_COMM_WORLD, &i);
- return i;
-#endif
-}
-
-void gmx_setup_nodecomm(FILE *fplog,t_commrec *cr)
-{
- gmx_nodecomm_t *nc;
- int n,rank,resultlen,hostnum,i,j,ng,ni;
-#ifdef GMX_MPI
- char mpi_hostname[MPI_MAX_PROCESSOR_NAME],num[MPI_MAX_PROCESSOR_NAME];
-#endif
-
- /* Many MPI implementations do not optimize MPI_Allreduce
- * (and probably also other global communication calls)
- * for multi-core nodes connected by a network.
- * We can optimize such communication by using one MPI call
- * within each node and one between the nodes.
- * For MVAPICH2 and Intel MPI this reduces the time for
- * the global_stat communication by 25%
- * for 2x2-core 3 GHz Woodcrest connected by mixed DDR/SDR Infiniband.
- * B. Hess, November 2007
- */
-
- nc = &cr->nc;
-
- nc->bUse = FALSE;
-#ifndef GMX_THREADS
- if (getenv("GMX_NO_NODECOMM") == NULL) {
-#ifdef GMX_MPI
- MPI_Comm_size(cr->mpi_comm_mygroup,&n);
- MPI_Comm_rank(cr->mpi_comm_mygroup,&rank);
- MPI_Get_processor_name(mpi_hostname,&resultlen);
- /* This procedure can only differentiate nodes with host names
- * that end on unique numbers.
- */
- i = 0;
- j = 0;
- /* Only parse the host name up to the first dot */
- while(i < resultlen && mpi_hostname[i] != '.') {
- if (isdigit(mpi_hostname[i])) {
- num[j++] = mpi_hostname[i];
- }
- i++;
- }
- num[j] = '\0';
- if (j == 0) {
- hostnum = 0;
- } else {
- /* Use only the last 9 decimals, so we don't overflow an int */
- hostnum = strtol(num + max(0,j-9), NULL, 10);
- }
-
- if (debug) {
- fprintf(debug,
- "In gmx_setup_nodecomm: splitting communicator of size %d\n",
- n);
- fprintf(debug,"In gmx_setup_nodecomm: hostname '%s', hostnum %d\n",
- mpi_hostname,hostnum);
- }
-
- /* The intra-node communicator, split on node number */
- MPI_Comm_split(cr->mpi_comm_mygroup,hostnum,rank,&nc->comm_intra);
- MPI_Comm_rank(nc->comm_intra,&nc->rank_intra);
- if (debug) {
- fprintf(debug,"In gmx_setup_nodecomm: node rank %d rank_intra %d\n",
- rank,nc->rank_intra);
- }
- /* The inter-node communicator, split on rank_intra.
- * We actually only need the one for rank=0,
- * but it is easier to create them all.
- */
- MPI_Comm_split(cr->mpi_comm_mygroup,nc->rank_intra,rank,&nc->comm_inter);
- /* Check if this really created two step communication */
- MPI_Comm_size(nc->comm_inter,&ng);
- MPI_Comm_size(nc->comm_intra,&ni);
- if (debug) {
- fprintf(debug,"In gmx_setup_nodecomm: groups %d, my group size %d\n",
- ng,ni);
- }
- if ((ng > 1 && ng < n) || (ni > 1 && ni < n)) {
- nc->bUse = TRUE;
- if (fplog)
- fprintf(fplog,"Using two step summing over %d groups of on average %.1f processes\n\n",ng,(real)n/(real)ng);
- if (nc->rank_intra > 0)
- MPI_Comm_free(&nc->comm_inter);
- } else {
- /* One group or all processes in a separate group, use normal summing */
- MPI_Comm_free(&nc->comm_inter);
- MPI_Comm_free(&nc->comm_intra);
- }
-#endif
- }
-#endif
-}
-
-void gmx_barrier(const t_commrec *cr)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_barrier");
-#else
- MPI_Barrier(cr->mpi_comm_mygroup);
-#endif
-}
-
-void gmx_abort(int noderank,int nnodes,int errorno)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_abort");
-#else
-#ifdef GMX_THREADS
- fprintf(stderr,"Halting program %s\n",ShortProgram());
- thanx(stderr);
- exit(1);
-#else
- if (nnodes > 1)
- {
- fprintf(stderr,"Halting parallel program %s on CPU %d out of %d\n",
- ShortProgram(),noderank,nnodes);
- }
- else
- {
- fprintf(stderr,"Halting program %s\n",ShortProgram());
- }
-
- thanx(stderr);
- MPI_Abort(MPI_COMM_WORLD,errorno);
- exit(1);
-#endif
-#endif
-}
-
-void gmx_bcast(int nbytes,void *b,const t_commrec *cr)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_bast");
-#else
- MPI_Bcast(b,nbytes,MPI_BYTE,MASTERRANK(cr),cr->mpi_comm_mygroup);
-#endif
-}
-
-void gmx_bcast_sim(int nbytes,void *b,const t_commrec *cr)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_bast");
-#else
- MPI_Bcast(b,nbytes,MPI_BYTE,MASTERRANK(cr),cr->mpi_comm_mysim);
-#endif
-}
-
-void gmx_sumd(int nr,double r[],const t_commrec *cr)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_sumd");
-#else
-#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
- if (cr->nc.bUse) {
- if (cr->nc.rank_intra == 0)
- {
- /* Use two step summing. */
- MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,0,
- cr->nc.comm_intra);
- /* Sum the roots of the internal (intra) buffers. */
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,
- cr->nc.comm_inter);
- }
- else
- {
- /* This is here because of the silly MPI specification
- that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
- MPI_Reduce(r,NULL,nr,MPI_DOUBLE,MPI_SUM,0,cr->nc.comm_intra);
- }
- MPI_Bcast(r,nr,MPI_DOUBLE,0,cr->nc.comm_intra);
- }
- else
- {
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,
- cr->mpi_comm_mygroup);
- }
-#else
- int i;
-
- if (nr > cr->mpb->dbuf_alloc) {
- cr->mpb->dbuf_alloc = nr;
- srenew(cr->mpb->dbuf,cr->mpb->dbuf_alloc);
- }
- if (cr->nc.bUse) {
- /* Use two step summing */
- MPI_Allreduce(r,cr->mpb->dbuf,nr,MPI_DOUBLE,MPI_SUM,cr->nc.comm_intra);
- if (cr->nc.rank_intra == 0) {
- /* Sum with the buffers reversed */
- MPI_Allreduce(cr->mpb->dbuf,r,nr,MPI_DOUBLE,MPI_SUM,
- cr->nc.comm_inter);
- }
- MPI_Bcast(r,nr,MPI_DOUBLE,0,cr->nc.comm_intra);
- } else {
- MPI_Allreduce(r,cr->mpb->dbuf,nr,MPI_DOUBLE,MPI_SUM,
- cr->mpi_comm_mygroup);
- for(i=0; i<nr; i++)
- r[i] = cr->mpb->dbuf[i];
- }
-#endif
-#endif
-}
-
-void gmx_sumf(int nr,float r[],const t_commrec *cr)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_sumf");
-#else
-#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
- if (cr->nc.bUse) {
- /* Use two step summing. */
- if (cr->nc.rank_intra == 0)
- {
- MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,0,
- cr->nc.comm_intra);
- /* Sum the roots of the internal (intra) buffers */
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,
- cr->nc.comm_inter);
- }
- else
- {
- /* This is here because of the silly MPI specification
- that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
- MPI_Reduce(r,NULL,nr,MPI_FLOAT,MPI_SUM,0,cr->nc.comm_intra);
- }
- MPI_Bcast(r,nr,MPI_FLOAT,0,cr->nc.comm_intra);
- }
- else
- {
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,cr->mpi_comm_mygroup);
- }
-#else
- int i;
-
- if (nr > cr->mpb->fbuf_alloc) {
- cr->mpb->fbuf_alloc = nr;
- srenew(cr->mpb->fbuf,cr->mpb->fbuf_alloc);
- }
- if (cr->nc.bUse) {
- /* Use two step summing */
- MPI_Allreduce(r,cr->mpb->fbuf,nr,MPI_FLOAT,MPI_SUM,cr->nc.comm_intra);
- if (cr->nc.rank_intra == 0) {
- /* Sum with the buffers reversed */
- MPI_Allreduce(cr->mpb->fbuf,r,nr,MPI_FLOAT,MPI_SUM,
- cr->nc.comm_inter);
- }
- MPI_Bcast(r,nr,MPI_FLOAT,0,cr->nc.comm_intra);
- } else {
- MPI_Allreduce(r,cr->mpb->fbuf,nr,MPI_FLOAT,MPI_SUM,
- cr->mpi_comm_mygroup);
- for(i=0; i<nr; i++)
- r[i] = cr->mpb->fbuf[i];
- }
-#endif
-#endif
-}
-
-void gmx_sumi(int nr,int r[],const t_commrec *cr)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_sumi");
-#else
-#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
- if (cr->nc.bUse) {
- /* Use two step summing */
- if (cr->nc.rank_intra == 0)
- {
- MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,0,cr->nc.comm_intra);
- /* Sum with the buffers reversed */
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,cr->nc.comm_inter);
- }
- else
- {
- /* This is here because of the silly MPI specification
- that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
- MPI_Reduce(r,NULL,nr,MPI_INT,MPI_SUM,0,cr->nc.comm_intra);
- }
- MPI_Bcast(r,nr,MPI_INT,0,cr->nc.comm_intra);
- }
- else
- {
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,cr->mpi_comm_mygroup);
- }
-#else
- int i;
-
- if (nr > cr->mpb->ibuf_alloc) {
- cr->mpb->ibuf_alloc = nr;
- srenew(cr->mpb->ibuf,cr->mpb->ibuf_alloc);
- }
- if (cr->nc.bUse) {
- /* Use two step summing */
- MPI_Allreduce(r,cr->mpb->ibuf,nr,MPI_INT,MPI_SUM,cr->nc.comm_intra);
- if (cr->nc.rank_intra == 0) {
- /* Sum with the buffers reversed */
- MPI_Allreduce(cr->mpb->ibuf,r,nr,MPI_INT,MPI_SUM,cr->nc.comm_inter);
- }
- MPI_Bcast(r,nr,MPI_INT,0,cr->nc.comm_intra);
- } else {
- MPI_Allreduce(r,cr->mpb->ibuf,nr,MPI_INT,MPI_SUM,cr->mpi_comm_mygroup);
- for(i=0; i<nr; i++)
- r[i] = cr->mpb->ibuf[i];
- }
-#endif
-#endif
-}
-
-#ifdef GMX_MPI
-void gmx_sumd_comm(int nr,double r[],MPI_Comm mpi_comm)
-{
-#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,mpi_comm);
-#else
- /* this function is only used in code that is not performance critical,
- (during setup, when comm_rec is not the appropriate communication
- structure), so this isn't as bad as it looks. */
- double *buf;
- int i;
-
- snew(buf, nr);
- MPI_Allreduce(r,buf,nr,MPI_DOUBLE,MPI_SUM,mpi_comm);
- for(i=0; i<nr; i++)
- r[i] = buf[i];
- sfree(buf);
-#endif
-}
-#endif
-
-#ifdef GMX_MPI
-void gmx_sumf_comm(int nr,float r[],MPI_Comm mpi_comm)
-{
-#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,mpi_comm);
-#else
- /* this function is only used in code that is not performance critical,
- (during setup, when comm_rec is not the appropriate communication
- structure), so this isn't as bad as it looks. */
- float *buf;
- int i;
-
- snew(buf, nr);
- MPI_Allreduce(r,buf,nr,MPI_FLOAT,MPI_SUM,mpi_comm);
- for(i=0; i<nr; i++)
- r[i] = buf[i];
- sfree(buf);
-#endif
-}
-#endif
-
-void gmx_sumd_sim(int nr,double r[],const gmx_multisim_t *ms)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_sumd");
-#else
- gmx_sumd_comm(nr,r,ms->mpi_comm_masters);
-#endif
-}
-
-void gmx_sumf_sim(int nr,float r[],const gmx_multisim_t *ms)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_sumf");
-#else
- gmx_sumf_comm(nr,r,ms->mpi_comm_masters);
-#endif
-}
-
-void gmx_sumi_sim(int nr,int r[], const gmx_multisim_t *ms)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_sumd");
-#else
-#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
- MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,ms->mpi_comm_masters);
-#else
- /* this is thread-unsafe, but it will do for now: */
- int i;
-
- if (nr > ms->mpb->ibuf_alloc) {
- ms->mpb->ibuf_alloc = nr;
- srenew(ms->mpb->ibuf,ms->mpb->ibuf_alloc);
- }
- MPI_Allreduce(r,ms->mpb->ibuf,nr,MPI_INT,MPI_SUM,ms->mpi_comm_masters);
- for(i=0; i<nr; i++)
- r[i] = ms->mpb->ibuf[i];
-#endif
-#endif
-}
-
-void gmx_finalize(void)
-{
-#ifndef GMX_MPI
- gmx_call("gmx_finalize");
-#else
- int ret;
-
- /* just as a check; we don't want to finalize twice */
- int finalized;
- MPI_Finalized(&finalized);
- if (finalized)
- return;
-
- /* We sync the processes here to try to avoid problems
- * with buggy MPI implementations that could cause
- * unfinished processes to terminate.
- */
- MPI_Barrier(MPI_COMM_WORLD);
-
- /*
- if (DOMAINDECOMP(cr)) {
- if (cr->npmenodes > 0 || cr->dd->bCartesian)
- MPI_Comm_free(&cr->mpi_comm_mygroup);
- if (cr->dd->bCartesian)
- MPI_Comm_free(&cr->mpi_comm_mysim);
- }
- */
-
- /* Apparently certain mpich implementations cause problems
- * with MPI_Finalize. In that case comment out MPI_Finalize.
- */
- if (debug)
- fprintf(debug,"Will call MPI_Finalize now\n");
-
- ret = MPI_Finalize();
- if (debug)
- fprintf(debug,"Return code from MPI_Finalize = %d\n",ret);
-#endif
-}
-
+++ /dev/null
-
-AM_CPPFLAGS= -I$(top_srcdir)/include -DGMXLIBDIR=\"$(datadir)/top\"
-
-if GMX_IA32_SSE
- IA32_SSE = nb_kernel_ia32_sse
- IA32_SSE_OBJ = nb_kernel_ia32_sse/libnb_kernel_ia32_sse.la
-endif
-
-if GMX_IA32_SSE2
- IA32_SSE2 = nb_kernel_ia32_sse2
- IA32_SSE2_OBJ = nb_kernel_ia32_sse2/libnb_kernel_ia32_sse2.la
-endif
-
-if GMX_X86_64_SSE
- X86_64_SSE = nb_kernel_x86_64_sse
- X86_64_SSE_OBJ = nb_kernel_x86_64_sse/libnb_kernel_x86_64_sse.la
-endif
-
-if GMX_X86_64_SSE2
- X86_64_SSE2 = nb_kernel_x86_64_sse2
- X86_64_SSE2_OBJ = nb_kernel_x86_64_sse2/libnb_kernel_x86_64_sse2.la
-endif
-
-if GMX_PPC_ALTIVEC
- PPC_ALTIVEC = nb_kernel_ppc_altivec
- PPC_ALTIVEC_OBJ = nb_kernel_ppc_altivec/libnb_kernel_ppc_altivec.la
-endif
-
-if GMX_IA64_ASM
-if GMX_DOUBLE
- IA64_DOUBLE = nb_kernel_ia64_double
- IA64_DOUBLE_OBJ = nb_kernel_ia64_double/libnb_kernel_ia64_double.la
-else
- IA64_SINGLE = nb_kernel_ia64_single
- IA64_SINGLE_OBJ = nb_kernel_ia64_single/libnb_kernel_ia64_single.la
-endif
-endif
-
-if GMX_BLUEGENE
- BLUEGENE = nb_kernel_bluegene
- BLUEGENE_OBJ = nb_kernel_bluegene/libnb_kernel_bluegene.la
-endif
-
-if GMX_POWER6
- POWER6 = nb_kernel_power6
- POWER6_OBJ = nb_kernel_power6/libnb_kernel_power6.la
-endif
-
-
-if GMX_FORTRAN
-if GMX_DOUBLE
- F77_DOUBLE = nb_kernel_f77_double
- F77_DOUBLE_OBJ = nb_kernel_f77_double/libnb_kernel_f77_double.la
-else
- F77_SINGLE = nb_kernel_f77_single
- F77_SINGLE_OBJ = nb_kernel_f77_single/libnb_kernel_f77_single.la
-endif
-endif
-
-
-
-SUBDIRS = $(IA32_SSE) $(IA32_SSE2) $(IA32_3DNOW) \
- $(X86_64_SSE) $(X86_64_SSE2) $(PPC_ALTIVEC) \
- $(IA64_SINGLE) $(IA64_DOUBLE) $(BLUEGENE) \
- $(POWER6) $(F77_DOUBLE) $(F77_SINGLE) \
- nb_kernel_c
-
-
-# Convenience library for nonbonded interactions - not installed.
-
-noinst_LTLIBRARIES = libnonbonded.la
-
-# Subdirectories will contain libtool convenience libraries.
-# These are not installed, but added to the main library.
-# We also add conditional objects directly to the main library.
-libnonbonded_la_LIBADD = \
- nb_kernel_c/libnb_kernel_c.la \
- $(IA32_SSE_OBJ) $(IA32_SSE2_OBJ) $(IA32_3DNOW_OBJ) \
- $(X86_64_SSE_OBJ) $(X86_64_SSE2_OBJ) $(PPC_ALTIVEC_OBJ) \
- $(IA64_SINGLE_OBJ) $(IA64_DOUBLE_OBJ) $(BLUEGENE_OBJ) \
- $(POWER6_OBJ) $(F77_DOUBLE_OBJ) $(F77_SINGLE_OBJ)
-
-
-libnonbonded_la_SOURCES = \
- nb_kerneltype.h nonbonded.c \
- nb_free_energy.c nb_free_energy.h \
- nb_generic.c nb_generic.h \
- nb_generic_cg.c nb_generic_cg.h
-
-
-
+++ /dev/null
-.deps
-.libs
+++ /dev/null
-.deps
-.libs
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
+++ /dev/null
-.deps
-.libs
+++ /dev/null
-# Convenience library for selection routines - not installed
-
-AM_YFLAGS=-d
-AM_CPPFLAGS= -I$(top_srcdir)/include
-
-noinst_LTLIBRARIES = libselection.la
-
-libselection_la_SOURCES = \
- compiler.c evaluate.c evaluate.h keywords.h \
- mempool.c mempool.h \
- params.c parser.c parser.h parsetree.c \
- parsetree.h \
- scanner.c scanner.h scanner_flex.h scanner_internal.c \
- scanner_internal.h selcollection.h selhelp.c \
- selhelp.h selection.c \
- selelem.c selelem.h selmethod.c selvalue.c \
- sm_compare.c sm_distance.c sm_insolidangle.c \
- sm_keywords.c sm_merge.c sm_permute.c sm_position.c \
- sm_same.c sm_simple.c \
- symrec.c symrec.h
-
-LDADD = ../libgmx@LIBSUFFIX@.la ../../mdlib/libmd@LIBSUFFIX@.la
-
-EXTRA_PROGRAMS = test_selection
-
-CLEANFILES = *.la *~ \\\#*
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Selection compilation and optimization.
- *
- * \todo
- * Better error handling and memory management in error situations.
- * At least, the main compilation function leaves the selection collection in
- * a bad state if an error occurs.
- *
- * \todo
- * The memory usage could still be optimized.
- * Use of memory pooling could still be extended, and a lot of redundant
- * gmin/gmax data could be eliminated for complex arithmetic expressions.
- */
-/*! \internal
- * \page selcompiler Selection compilation
- *
- * The compiler takes the selection element tree from the selection parser
- * (see \ref selparser) as input. The selection parser is quite independent of
- * selection evaluation details, and the compiler processes the tree to
- * conform to what the evaluation functions expect.
- * For better control and optimization possibilities, the compilation is
- * done on all selections simultaneously.
- * Hence, all the selections should be parsed before the compiler can be
- * called.
- *
- * The compiler initializes all fields in \c t_selelem not initialized by
- * the parser: \c t_selelem::v (some fields have already been initialized by
- * the parser), \c t_selelem::evaluate, and \c t_selelem::u (again, some
- * elements have been initialized in the parser).
- * The \c t_selelem::cdata field is used during the compilation to store
- * internal data, but the data is freed when the compiler returns.
- *
- * In addition to initializing the elements, the compiler reorganizes the tree
- * to simplify and optimize evaluation. The compiler also evaluates the static
- * parts of the selection: in the end of the compilation, static parts have
- * been replaced by the result of the evaluation.
- *
- * The compiler is called by calling gmx_ana_selcollection_compile().
- * This functions then does the compilation in several passes over the
- * \c t_selelem tree.
- * -# Subexpressions are extracted: a separate root is created for each
- * subexpression, and placed before the expression is first used.
- * Currently, only variables and expressions used to evaluate parameter
- * values are extracted, but common subexpression could also be detected
- * here.
- * -# A second pass with simple reordering and initialization is done:
- * -# Boolean expressions are combined such that one element can evaluate,
- * e.g., "A and B and C". The subexpressions in gmx_boolean expression are
- * reordered such that static expressions come first without otherwise
- * altering the relative order of the expressions.
- * -# The \c t_selelem::evaluate field is set to the correct evaluation
- * function from evaluate.h.
- * -# The compiler data structure is allocated for each element, and
- * the fields are initialized, with the exception of the contents of
- * \c gmax and \c gmin fields. In reality, several passes are made
- * to completely initialize the structure, because some flags are set
- * recursively based on which elements refer to an element, and these
- * flags need to be set to initialize other fields.
- * .
- * -# The evaluation function of all elements is replaced with the
- * analyze_static() function to be able to initialize the element before
- * the actual evaluation function is called.
- * The evaluation machinery is then called to initialize the whole tree,
- * while simultaneously evaluating the static expressions.
- * During the evaluation, track is kept of the smallest and largest
- * possible selections, and these are stored in the internal compiler
- * data structure for each element.
- * To be able to do this for all possible values of dynamical expressions,
- * special care needs to be taken with gmx_boolean expressions because they
- * are short-circuiting. This is done through the
- * \c SEL_CDATA_EVALMAX flag, which makes dynamic child expressions
- * of \c BOOL_OR expressions evaluate to empty groups, while subexpressions
- * of \c BOOL_AND are evaluated to largest possible groups.
- * Memory is also allocated to store the results of the evaluation.
- * For each element, analyze_static() calls the actual evaluation function
- * after the element has been properly initialized.
- * -# Another evaluation pass is done over subexpressions with more than
- * one reference to them. These cannot be completely processed during the
- * first pass, because it is not known whether later references require
- * additional evaluation of static expressions.
- * -# Unused subexpressions are removed. For efficiency reasons (and to avoid
- * some checks), this is actually done several times already earlier in
- * the compilation process.
- * -# Most of the processing is now done, and the next pass simply sets the
- * evaluation group of root elements to the largest selection as determined
- * in pass 3. For root elements of subexpressions that should not be
- * evaluated before they are referred to, the evaluation group/function is
- * cleared. At the same time, position calculation data is initialized for
- * for selection method elements that require it. Compiler data is also
- * freed as it is no longer needed.
- * -# A final pass initializes the total masses and charges in the
- * \c gmx_ana_selection_t data structures.
- *
- * The actual evaluation of the selection is described in the documentation
- * of the functions in evaluate.h.
- *
- * \todo
- * Some combinations of method parameter flags are not yet properly treated by
- * the compiler or the evaluation functions in evaluate.c. All the ones used by
- * currently implemented methods should work, but new combinations might not.
- *
- *
- * \section selcompiler_tree Element tree after compilation
- *
- * After the compilation, the selection element tree is suitable for
- * gmx_ana_selcollection_evaluate().
- * Enough memory has been allocated for \ref t_selelem::v
- * (and \ref t_selelem::cgrp for \ref SEL_SUBEXPR elements) to allow the
- * selection to be evaluated without allocating any memory.
- *
- *
- * \subsection selcompiler_tree_root Root elements
- *
- * The top level of the tree consists of a chain of \ref SEL_ROOT elements.
- * These are used for two purposes:
- * -# A selection that should be evaluated.
- * These elements appear in the same order as the selections in the input.
- * For these elements, \ref t_selelem::v has been set to the maximum
- * possible group that the selection can evaluate to (only for dynamic
- * selections), and \ref t_selelem::cgrp has been set to use a NULL group
- * for evaluation.
- * -# A subexpression that appears in one or more selections.
- * Each selection that gives a value for a method parameter is a
- * potential subexpression, as is any variable value.
- * Only subexpressions that require evaluation for each frame are left
- * after the selection is compiled.
- * Each subexpression appears in the chain before any references to it.
- * For these elements, \c t_selelem::cgrp has been set to the group
- * that should be used to evaluate the subexpression.
- * If \c t_selelem::cgrp is empty, the total evaluation group is not known
- * in advance or it is more efficient to evaluate the subexpression only
- * when it is referenced. If this is the case, \c t_selelem::evaluate is
- * also NULL.
- *
- * The children of the \ref SEL_ROOT elements can be used to distinguish
- * the two types of root elements from each other; the rules are the same
- * as for the parsed tree (see \ref selparser_tree_root).
- * Subexpressions are treated as if they had been provided through variables.
- *
- * Selection names are stored as after parsing (see \ref selparser_tree_root).
- *
- *
- * \subsection selcompiler_tree_const Constant elements
- *
- * All (sub)selections that do not require particle positions have been
- * replaced with \ref SEL_CONST elements.
- * Constant elements from the parser are also retained if present in
- * dynamic parts of the selections.
- * Several constant elements with a NULL \c t_selelem::evaluate are left for
- * debugging purposes; of these, only the ones for \ref BOOL_OR expressions are
- * used during evaluation.
- *
- * The value is stored in \c t_selelem::v, and for group values with an
- * evaluation function set, also in \c t_selelem::cgrp.
- * For \ref GROUP_VALUE elements, unnecessary atoms (i.e., atoms that
- * could never be selected) have been removed from the value.
- *
- * \ref SEL_CONST elements have no children.
- *
- *
- * \subsection selcompiler_tree_method Method evaluation elements
- *
- * All selection methods that need to be evaluated dynamically are described
- * by a \ref SEL_EXPRESSION element. The \c t_selelem::method and
- * \c t_selelem::mdata fields have already been initialized by the parser,
- * and the compiler only calls the initialization functions in the method
- * data structure to do some additional initialization of these fields at
- * appropriate points. If the \c t_selelem::pc data field has been created by
- * the parser, the compiler initializes the data structure properly once the
- * required positions are known. If the \c t_selelem::pc field is NULL after
- * the parser, but the method provides only sel_updatefunc_pos(), an
- * appropriate position calculation data structure is created.
- * If \c t_selelem::pc is not NULL, \c t_selelem::pos is also initialized
- * to hold the positions calculated.
- *
- * Children of these elements are of type \ref SEL_SUBEXPRREF, and describe
- * parameter values that need to be evaluated for each frame. See the next
- * section for more details.
- * \ref SEL_CONST children can also appear, and stand for parameters that get
- * their value from a static expression. These elements are present only for
- * debugging purposes: they always have a NULL evaluation function.
- *
- *
- * \subsection selcompiler_tree_subexpr Subexpression elements
- *
- * As described in \ref selcompiler_tree_root, subexpressions are created
- * for each variable and each expression that gives a value to a selection
- * method parameter. As the only child of the \ref SEL_ROOT element,
- * these elements have a \ref SEL_SUBEXPR element. The \ref SEL_SUBEXPR
- * element has a single child, which evaluates the actual expression.
- * After compilation, only subexpressions that require particle positions
- * for evaluation are left.
- * For non-variable subexpression, automatic names have been generated to
- * help in debugging.
- *
- * For \ref SEL_SUBEXPR elements, memory has been allocated for
- * \c t_selelem::cgrp to store the group for which the expression has been
- * evaluated during the current frame. This is only done if full subexpression
- * evaluation by _gmx_sel_evaluate_subexpr() is needed; the other evaluation
- * functions do not require this memory.
- *
- * \ref SEL_SUBEXPRREF elements are used to describe references to
- * subexpressions. They have always a single child, which is the
- * \ref SEL_SUBEXPR element being referenced.
- *
- * If a subexpression is used only once, the evaluation has been optimized by
- * setting the child of the \ref SEL_SUBEXPR element to evaluate the value of
- * \ref SEL_SUBEXPRREF directly (in the case of memory pooling, this is managed
- * by the evaluation functions). In such cases, the evaluation routines for the
- * \ref SEL_SUBEXPRREF and \ref SEL_SUBEXPR elements only propagate some status
- * information, but do not unnecessarily copy the values.
- *
- *
- * \subsection selcompiler_tree_gmx_bool Boolean elements
- *
- * \ref SEL_BOOLEAN elements have been merged such that one element
- * may carry out evaluation of more than one operation of the same type.
- * The static parts of the expressions have been evaluated, and are placed
- * in the first child. These are followed by the dynamic expressions, in the
- * order provided by the user.
- *
- *
- * \subsection selcompiler_tree_arith Arithmetic elements
- *
- * Constant and static expressions in \ref SEL_ARITHMETIC elements have been
- * calculated.
- * Currently, no other processing is done.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <math.h>
-#include <stdarg.h>
-
-#include <smalloc.h>
-#include <string2.h>
-#include <vec.h>
-
-#include <indexutil.h>
-#include <poscalc.h>
-#include <selection.h>
-#include <selmethod.h>
-
-#include "evaluate.h"
-#include "keywords.h"
-#include "mempool.h"
-#include "selcollection.h"
-#include "selelem.h"
-
-/*! \internal \brief
- * Compiler flags.
- */
-enum
-{
- /*! \brief
- * Whether a subexpression needs to evaluated for all atoms.
- *
- * This flag is set for \ref SEL_SUBEXPR elements that are used to
- * evaluate non-atom-valued selection method parameters, as well as
- * those that are used directly as values of selections.
- */
- SEL_CDATA_FULLEVAL = 1,
- /*! \brief
- * Whether the whole subexpression should be treated as static.
- *
- * This flag is always FALSE if \ref SEL_DYNAMIC is set for the element,
- * but it is also FALSE for static elements within common subexpressions.
- */
- SEL_CDATA_STATIC = 2,
- /** Whether the subexpression will always be evaluated in the same group. */
- SEL_CDATA_STATICEVAL = 4,
- /** Whether the compiler evaluation routine should return the maximal selection. */
- SEL_CDATA_EVALMAX = 8,
- /** Whether memory has been allocated for \p gmin and \p gmax. */
- SEL_CDATA_MINMAXALLOC = 16,
- /** Whether subexpressions use simple pass evaluation functions. */
- SEL_CDATA_SIMPLESUBEXPR = 32,
- /** Whether this expressions is a part of a common subexpression. */
- SEL_CDATA_COMMONSUBEXPR = 64
-};
-
-/*! \internal \brief
- * Internal data structure used by the compiler.
- */
-typedef struct t_compiler_data
-{
- /** The real evaluation method. */
- sel_evalfunc evaluate;
- /** Flags for specifying how to treat this element during compilation. */
- int flags;
- /** Smallest selection that can be selected by the subexpression. */
- gmx_ana_index_t *gmin;
- /** Largest selection that can be selected by the subexpression. */
- gmx_ana_index_t *gmax;
-} t_compiler_data;
-
-
-/********************************************************************
- * COMPILER UTILITY FUNCTIONS
- ********************************************************************/
-
-static void
-print_group_info(FILE *fp, const char *name, t_selelem *sel, gmx_ana_index_t *g)
-{
- fprintf(fp, " %s=", name);
- if (!g)
- {
- fprintf(fp, "(null)");
- }
- else if (sel->cdata->flags & SEL_CDATA_MINMAXALLOC)
- {
- fprintf(fp, "(%d atoms, %p)", g->isize, (void*)g);
- }
- else if (sel->v.type == GROUP_VALUE && g == sel->v.u.g)
- {
- fprintf(fp, "(static, %p)", (void*)g);
- }
- else
- {
- fprintf(fp, "%p", (void*)g);
- }
-}
-
-/*!
- * \param[in] fp File handle to receive the output.
- * \param[in] sel Selection element to print.
- * \param[in] level Indentation level, starting from zero.
- */
-void
-_gmx_selelem_print_compiler_info(FILE *fp, t_selelem *sel, int level)
-{
- if (!sel->cdata)
- {
- return;
- }
- fprintf(fp, "%*c cdata: flg=", level*2+1, ' ');
- if (sel->cdata->flags & SEL_CDATA_FULLEVAL)
- {
- fprintf(fp, "F");
- }
- if (!(sel->cdata->flags & SEL_CDATA_STATIC))
- {
- fprintf(fp, "D");
- }
- if (sel->cdata->flags & SEL_CDATA_STATICEVAL)
- {
- fprintf(fp, "S");
- }
- if (sel->cdata->flags & SEL_CDATA_EVALMAX)
- {
- fprintf(fp, "M");
- }
- if (sel->cdata->flags & SEL_CDATA_MINMAXALLOC)
- {
- fprintf(fp, "A");
- }
- if (sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
- {
- fprintf(fp, "Ss");
- }
- if (sel->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
- {
- fprintf(fp, "Sc");
- }
- if (!sel->cdata->flags)
- {
- fprintf(fp, "0");
- }
- fprintf(fp, " eval=");
- _gmx_sel_print_evalfunc_name(fp, sel->cdata->evaluate);
- print_group_info(fp, "gmin", sel, sel->cdata->gmin);
- print_group_info(fp, "gmax", sel, sel->cdata->gmax);
- fprintf(fp, "\n");
-}
-
-/*!
- * \param sel Selection to free.
- *
- * This function only frees the data for the given selection, not its children.
- * It is safe to call the function when compiler data has not been allocated
- * or has already been freed; in such a case, nothing is done.
- */
-void
-_gmx_selelem_free_compiler_data(t_selelem *sel)
-{
- if (sel->cdata)
- {
- sel->evaluate = sel->cdata->evaluate;
- if (sel->cdata->flags & SEL_CDATA_MINMAXALLOC)
- {
- sel->cdata->gmin->name = NULL;
- sel->cdata->gmax->name = NULL;
- gmx_ana_index_deinit(sel->cdata->gmin);
- gmx_ana_index_deinit(sel->cdata->gmax);
- sfree(sel->cdata->gmin);
- sfree(sel->cdata->gmax);
- }
- sfree(sel->cdata);
- }
- sel->cdata = NULL;
-}
-
-/*! \brief
- * Allocates memory for storing the evaluated value of a selection element.
- *
- * \param sel Selection element to initialize
- * \param[in] isize Maximum evaluation group size.
- * \param[in] bChildEval TRUE if children have already been processed.
- * \returns TRUE if the memory was allocated, FALSE if children need to
- * be processed first.
- *
- * If called more than once, memory is (re)allocated to ensure that the
- * maximum of the \p isize values can be stored.
- */
-static gmx_bool
-alloc_selection_data(t_selelem *sel, int isize, gmx_bool bChildEval)
-{
- int nalloc;
-
- if (sel->mempool)
- {
- return TRUE;
- }
- /* Find out the number of elements to allocate */
- if (sel->flags & SEL_SINGLEVAL)
- {
- nalloc = 1;
- }
- else if (sel->flags & SEL_ATOMVAL)
- {
- nalloc = isize;
- }
- else /* sel->flags should contain SEL_VARNUMVAL */
- {
- t_selelem *child;
-
- if (!bChildEval)
- {
- return FALSE;
- }
- child = (sel->type == SEL_SUBEXPRREF ? sel->child : sel);
- if (child->type == SEL_SUBEXPR)
- {
- child = child->child;
- }
- nalloc = (sel->v.type == POS_VALUE) ? child->v.u.p->nr : child->v.nr;
- }
- /* For positions, we actually want to allocate just a single structure
- * for nalloc positions. */
- if (sel->v.type == POS_VALUE)
- {
- isize = nalloc;
- nalloc = 1;
- }
- /* Allocate memory for sel->v.u if needed */
- if (sel->flags & SEL_ALLOCVAL)
- {
- _gmx_selvalue_reserve(&sel->v, nalloc);
- }
- /* Reserve memory inside group and position structures if
- * SEL_ALLOCDATA is set. */
- if (sel->flags & SEL_ALLOCDATA)
- {
- if (sel->v.type == GROUP_VALUE)
- {
- gmx_ana_index_reserve(sel->v.u.g, isize);
- }
- else if (sel->v.type == POS_VALUE)
- {
- gmx_ana_pos_reserve(sel->v.u.p, isize, 0);
- }
- }
- return TRUE;
-}
-
-/*! \brief
- * Replace the evaluation function of each element in the subtree.
- *
- * \param sel Root of the selection subtree to process.
- * \param[in] eval The new evaluation function.
- */
-static void
-set_evaluation_function(t_selelem *sel, sel_evalfunc eval)
-{
- sel->evaluate = eval;
- if (sel->type != SEL_SUBEXPRREF)
- {
- t_selelem *child = sel->child;
- while (child)
- {
- set_evaluation_function(child, eval);
- child = child->next;
- }
- }
-}
-
-
-/********************************************************************
- * SUBEXPRESSION PROCESSING
- ********************************************************************/
-
-/*! \brief
- * Reverses the chain of selection elements starting at \p root.
- *
- * \param root First selection in the whole selection chain.
- * \returns The new first element for the chain.
- */
-static t_selelem *
-reverse_selelem_chain(t_selelem *root)
-{
- t_selelem *item;
- t_selelem *prev;
- t_selelem *next;
-
- prev = NULL;
- item = root;
- while (item)
- {
- next = item->next;
- item->next = prev;
- prev = item;
- item = next;
- }
- return prev;
-}
-
-/*! \brief
- * Removes subexpressions that don't have any references.
- *
- * \param root First selection in the whole selection chain.
- * \returns The new first element for the chain.
- *
- * The elements are processed in reverse order to correctly detect
- * subexpressions only referred to by other subexpressions.
- */
-static t_selelem *
-remove_unused_subexpressions(t_selelem *root)
-{
- t_selelem *item;
- t_selelem *prev;
- t_selelem *next;
-
- root = reverse_selelem_chain(root);
- while (root->child->type == SEL_SUBEXPR && root->child->refcount == 1)
- {
- next = root->next;
- _gmx_selelem_free(root);
- root = next;
- }
- prev = root;
- item = root->next;
- while (item)
- {
- next = item->next;
- if (item->child->type == SEL_SUBEXPR && item->child->refcount == 1)
- {
- prev->next = next;
- _gmx_selelem_free(item);
- }
- else
- {
- prev = item;
- }
- item = next;
- }
- return reverse_selelem_chain(root);
-}
-
-/*! \brief
- * Creates a name with a running number for a subexpression.
- *
- * \param[in,out] sel The subexpression to be named.
- * \param[in] i Running number for the subexpression.
- *
- * The name of the selection becomes "SubExpr N", where N is \p i;
- * Memory is allocated for the name and the name is stored both in
- * \c t_selelem::name and \c t_selelem::u::cgrp::name; the latter
- * is freed by _gmx_selelem_free().
- */
-static void
-create_subexpression_name(t_selelem *sel, int i)
-{
- int len, ret;
- char *name;
-
- len = 8 + (int)log10(abs(i)) + 3;
- snew(name, len+1);
- /* FIXME: snprintf used to be used here for extra safety, but this
- * requires extra checking on Windows since it only provides a
- * non-C99-conforming implementation as _snprintf()... */
- ret = sprintf(name, "SubExpr %d", i);
- if (ret < 0 || ret > len)
- {
- sfree(name);
- name = NULL;
- }
- sel->name = name;
- sel->u.cgrp.name = name;
-}
-
-/*! \brief
- * Processes and extracts subexpressions from a given selection subtree.
- *
- * \param sel Root of the subtree to process.
- * \param subexprn Pointer to a subexpression counter.
- * \returns Pointer to a chain of subselections, or NULL if none were found.
- *
- * This function finds recursively all \ref SEL_SUBEXPRREF elements below
- * the given root element and ensures that their children are within
- * \ref SEL_SUBEXPR elements. It also creates a chain of \ref SEL_ROOT elements
- * that contain the subexpression as their children and returns the first
- * of these root elements.
- */
-static t_selelem *
-extract_item_subselections(t_selelem *sel, int *subexprn)
-{
- t_selelem *root;
- t_selelem *subexpr;
- t_selelem *child;
-
- root = subexpr = NULL;
- child = sel->child;
- while (child)
- {
- if (!root)
- {
- root = subexpr = extract_item_subselections(child, subexprn);
- }
- else
- {
- subexpr->next = extract_item_subselections(child, subexprn);
- }
- while (subexpr && subexpr->next)
- {
- subexpr = subexpr->next;
- }
- /* The latter check excludes variable references.
- * It also excludes subexpression elements that have already been
- * processed, because they are given a name when they are first
- * encountered.
- * TODO: There should be a more robust mechanism (probably a dedicated
- * flag) for detecting parser-generated subexpressions than relying on
- * a NULL name field. */
- if (child->type == SEL_SUBEXPRREF && (child->child->type != SEL_SUBEXPR
- || child->child->name == NULL))
- {
- /* Create the root element for the subexpression */
- if (!root)
- {
- root = subexpr = _gmx_selelem_create(SEL_ROOT);
- }
- else
- {
- subexpr->next = _gmx_selelem_create(SEL_ROOT);
- subexpr = subexpr->next;
- }
- /* Create the subexpression element and/or
- * move the actual subexpression under the created element. */
- if (child->child->type != SEL_SUBEXPR)
- {
- subexpr->child = _gmx_selelem_create(SEL_SUBEXPR);
- _gmx_selelem_set_vtype(subexpr->child, child->v.type);
- subexpr->child->child = child->child;
- child->child = subexpr->child;
- }
- else
- {
- subexpr->child = child->child;
- }
- create_subexpression_name(subexpr->child, ++*subexprn);
- subexpr->child->refcount++;
- /* Set the flags for the created elements */
- subexpr->flags |= (child->flags & SEL_VALFLAGMASK);
- subexpr->child->flags |= (child->flags & SEL_VALFLAGMASK);
- }
- child = child->next;
- }
-
- return root;
-}
-
-/*! \brief
- * Extracts subexpressions of the selection chain.
- *
- * \param sel First selection in the whole selection chain.
- * \returns The new first element for the chain.
- *
- * Finds all the subexpressions (and their subexpressions) in the
- * selection chain starting from \p sel and creates \ref SEL_SUBEXPR
- * elements for them.
- * \ref SEL_ROOT elements are also created for each subexpression
- * and inserted into the selection chain before the expressions that
- * refer to them.
- */
-static t_selelem *
-extract_subexpressions(t_selelem *sel)
-{
- t_selelem *root, *item, *next;
- int subexprn;
-
- subexprn = 0;
- root = NULL;
- next = sel;
- while (next)
- {
- item = extract_item_subselections(next, &subexprn);
- if (item)
- {
- if (!root)
- {
- root = item;
- }
- else
- {
- sel->next = item;
- }
- while (item->next)
- {
- item = item->next;
- }
- item->next = next;
- }
- else if (!root)
- {
- root = next;
- }
- sel = next;
- next = next->next;
- }
- return root;
-}
-
-
-/********************************************************************
- * BOOLEAN OPERATION REORDERING
- ********************************************************************/
-
-/*! \brief
- * Removes redundant gmx_boolean selection elements.
- *
- * \param sel Root of the selection subtree to optimize.
- *
- * This function merges similar gmx_boolean operations (e.g., (A or B) or C becomes
- * a single OR operation with three operands).
- */
-static void
-optimize_gmx_boolean_expressions(t_selelem *sel)
-{
- t_selelem *child, *prev;
-
- /* Do recursively for children */
- if (sel->type != SEL_SUBEXPRREF)
- {
- prev = NULL;
- child = sel->child;
- while (child)
- {
- optimize_gmx_boolean_expressions(child);
- /* Remove double negations */
- if (child->type == SEL_BOOLEAN && child->u.boolt == BOOL_NOT
- && child->child->type == SEL_BOOLEAN && child->child->u.boolt == BOOL_NOT)
- {
- /* Move the doubly negated expression up two levels */
- if (!prev)
- {
- sel->child = child->child->child;
- prev = sel->child;
- }
- else
- {
- prev->next = child->child->child;
- prev = prev->next;
- }
- child->child->child->next = child->next;
- /* Remove the two negations */
- child->child->child = NULL;
- child->next = NULL;
- _gmx_selelem_free(child);
- child = prev;
- }
- prev = child;
- child = child->next;
- }
- }
- if (sel->type != SEL_BOOLEAN || sel->u.boolt == BOOL_NOT)
- {
- return;
- }
- /* Merge subsequent binary operations */
- prev = NULL;
- child = sel->child;
- while (child)
- {
- if (child->type == SEL_BOOLEAN && child->u.boolt == sel->u.boolt)
- {
- if (!prev)
- {
- sel->child = child->child;
- prev = sel->child;
- }
- else
- {
- prev->next = child->child;
- }
- while (prev->next)
- {
- prev = prev->next;
- }
- prev->next = child->next;
- sfree(child->v.u.g);
- sfree(child);
- child = prev->next;
- }
- else
- {
- prev = child;
- child = child->next;
- }
- }
-}
-
-/*! \brief
- * Reorders children of gmx_boolean expressions such that static selections
- * come first.
- *
- * \param sel Root of the selection subtree to reorder.
- *
- * The relative order of static expressions does not change.
- * The same is true for the dynamic expressions.
- */
-static void
-reorder_gmx_boolean_static_children(t_selelem *sel)
-{
- t_selelem *child, *prev, *next;
-
- /* Do recursively for children */
- if (sel->type != SEL_SUBEXPRREF)
- {
- child = sel->child;
- while (child)
- {
- reorder_gmx_boolean_static_children(child);
- child = child->next;
- }
- }
-
- /* Reorder gmx_boolean expressions such that static selections come first */
- if (sel->type == SEL_BOOLEAN && (sel->flags & SEL_DYNAMIC))
- {
- t_selelem start;
-
- start.next = sel->child;
- prev = &start;
- child = &start;
- while (child->next)
- {
- /* child is the last handled static expression */
- /* prev is the last handled non-static expression */
- next = prev->next;
- while (next && (next->flags & SEL_DYNAMIC))
- {
- prev = next;
- next = next->next;
- }
- /* next is now the first static expression after child */
- if (!next)
- {
- break;
- }
- /* Reorder such that next comes after child */
- if (prev != child)
- {
- prev->next = next->next;
- next->next = child->next;
- child->next = next;
- }
- else
- {
- prev = prev->next;
- }
- /* Advance child by one */
- child = next;
- }
-
- sel->child = start.next;
- }
-}
-
-
-/********************************************************************
- * ARITHMETIC EXPRESSION PROCESSING
- ********************************************************************/
-
-/*! \brief
- * Processes arithmetic expressions to simplify and speed up evaluation.
- *
- * \param sel Root of the selection subtree to process.
- *
- * Currently, this function only converts integer constants to reals
- * within arithmetic expressions.
- */
-static gmx_bool
-optimize_arithmetic_expressions(t_selelem *sel)
-{
- t_selelem *child;
- gmx_bool bOk;
-
- /* Do recursively for children. */
- if (sel->type != SEL_SUBEXPRREF)
- {
- child = sel->child;
- while (child)
- {
- bOk = optimize_arithmetic_expressions(child);
- if (!bOk)
- {
- return bOk;
- }
- child = child->next;
- }
- }
-
- if (sel->type != SEL_ARITHMETIC)
- {
- return TRUE;
- }
-
- /* Convert integer constants to reals. */
- child = sel->child;
- while (child)
- {
- if (child->v.type == INT_VALUE)
- {
- real *r;
-
- if (child->type != SEL_CONST)
- {
- gmx_impl("Non-constant integer expressions not implemented in arithmetic evaluation");
- return FALSE;
- }
- snew(r, 1);
- r[0] = child->v.u.i[0];
- sfree(child->v.u.i);
- child->v.u.r = r;
- child->v.type = REAL_VALUE;
- }
- else if (child->v.type != REAL_VALUE)
- {
- gmx_bug("Internal error");
- return FALSE;
- }
- child = child->next;
- }
- return TRUE;
-}
-
-
-/********************************************************************
- * EVALUATION PREPARATION COMPILER
- ********************************************************************/
-
-/*! \brief
- * Sets the evaluation functions for the selection (sub)tree.
- *
- * \param[in,out] sel Root of the selection subtree to process.
- * \returns TRUE on success, FALSE if any subexpression fails.
- *
- * This function sets the evaluation function (\c t_selelem::evaluate)
- * for the selection elements.
- */
-static gmx_bool
-init_item_evalfunc(t_selelem *sel)
-{
- /* Process children. */
- if (sel->type != SEL_SUBEXPRREF)
- {
- t_selelem *child;
-
- child = sel->child;
- while (child)
- {
- if (!init_item_evalfunc(child))
- {
- return FALSE;
- }
- child = child->next;
- }
- }
-
- /* Set the evaluation function */
- switch (sel->type)
- {
- case SEL_CONST:
- if (sel->v.type == GROUP_VALUE)
- {
- sel->evaluate = &_gmx_sel_evaluate_static;
- }
- break;
-
- case SEL_EXPRESSION:
- if (!(sel->flags & SEL_DYNAMIC) && sel->u.expr.method
- && sel->u.expr.method->init_frame)
- {
- sel->flags |= SEL_INITFRAME;
- }
- sel->evaluate = &_gmx_sel_evaluate_method;
- break;
-
- case SEL_ARITHMETIC:
- sel->evaluate = &_gmx_sel_evaluate_arithmetic;
- break;
-
- case SEL_MODIFIER:
- if (sel->v.type != NO_VALUE)
- {
- sel->evaluate = &_gmx_sel_evaluate_modifier;
- }
- break;
-
- case SEL_BOOLEAN:
- switch (sel->u.boolt)
- {
- case BOOL_NOT: sel->evaluate = &_gmx_sel_evaluate_not; break;
- case BOOL_AND: sel->evaluate = &_gmx_sel_evaluate_and; break;
- case BOOL_OR: sel->evaluate = &_gmx_sel_evaluate_or; break;
- case BOOL_XOR:
- gmx_impl("xor expressions not implemented");
- return FALSE;
- }
- break;
-
- case SEL_ROOT:
- sel->evaluate = &_gmx_sel_evaluate_root;
- break;
-
- case SEL_SUBEXPR:
- sel->evaluate = (sel->refcount == 2
- ? &_gmx_sel_evaluate_subexpr_simple
- : &_gmx_sel_evaluate_subexpr);
- break;
-
- case SEL_SUBEXPRREF:
- sel->name = sel->child->name;
- sel->evaluate = (sel->child->refcount == 2
- ? &_gmx_sel_evaluate_subexprref_simple
- : &_gmx_sel_evaluate_subexprref);
- break;
- }
-
- return TRUE;
-}
-
-/*! \brief
- * Sets the memory pool for selection elements that can use it.
- *
- * \param sel Root of the selection subtree to process.
- * \param[in] mempool Memory pool to use.
- */
-static void
-setup_memory_pooling(t_selelem *sel, gmx_sel_mempool_t *mempool)
-{
- if (sel->type != SEL_SUBEXPRREF)
- {
- t_selelem *child;
-
- child = sel->child;
- while (child)
- {
- if ((sel->type == SEL_BOOLEAN && (child->flags & SEL_DYNAMIC))
- || (sel->type == SEL_ARITHMETIC && child->type != SEL_CONST
- && !(child->flags & SEL_SINGLEVAL))
- || (sel->type == SEL_SUBEXPR && sel->refcount > 2))
- {
- child->mempool = mempool;
- if (child->type == SEL_SUBEXPRREF
- && child->child->refcount == 2)
- {
- child->child->child->mempool = mempool;
- }
- }
- setup_memory_pooling(child, mempool);
- child = child->next;
- }
- }
-}
-
-/*! \brief
- * Prepares the selection (sub)tree for evaluation.
- *
- * \param[in,out] sel Root of the selection subtree to prepare.
- *
- * It also allocates memory for the \p sel->v.u.g or \p sel->v.u.p
- * structure if required.
- */
-static void
-init_item_evaloutput(t_selelem *sel)
-{
- /* Process children. */
- if (sel->type != SEL_SUBEXPRREF)
- {
- t_selelem *child;
-
- child = sel->child;
- while (child)
- {
- init_item_evaloutput(child);
- child = child->next;
- }
- }
-
- if (sel->type == SEL_SUBEXPR && sel->refcount == 2)
- {
- sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
- {
- _gmx_selvalue_setstore(&sel->v, sel->child->v.u.ptr);
- }
- }
- else if (sel->type == SEL_SUBEXPR
- && (sel->cdata->flags & SEL_CDATA_FULLEVAL))
- {
- sel->evaluate = &_gmx_sel_evaluate_subexpr_staticeval;
- sel->cdata->evaluate = sel->evaluate;
- sel->child->mempool = NULL;
- sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
- {
- _gmx_selvalue_setstore(&sel->v, sel->child->v.u.ptr);
- }
- }
- else if (sel->type == SEL_SUBEXPRREF && sel->child->refcount == 2)
- {
- if (sel->v.u.ptr)
- {
- _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
- _gmx_selelem_free_values(sel->child->child);
- sel->child->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- sel->child->child->flags |= (sel->flags & SEL_ALLOCDATA);
- _gmx_selvalue_setstore(&sel->child->child->v, sel->v.u.ptr);
- }
- else if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
- {
- _gmx_selvalue_setstore(&sel->v, sel->child->child->v.u.ptr);
- }
- sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- }
-
- /* Make sure that the group/position structure is allocated. */
- if (!sel->v.u.ptr && (sel->flags & SEL_ALLOCVAL))
- {
- if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
- {
- _gmx_selvalue_reserve(&sel->v, 1);
- sel->v.nr = 1;
- }
- }
-}
-
-
-/********************************************************************
- * COMPILER DATA INITIALIZATION
- ********************************************************************/
-
-/*! \brief
- * Allocates memory for the compiler data and initializes the structure.
- *
- * \param sel Root of the selection subtree to process.
- */
-static void
-init_item_compilerdata(t_selelem *sel)
-{
- t_selelem *child;
-
- /* Allocate the compiler data structure */
- snew(sel->cdata, 1);
-
- /* Store the real evaluation method because the compiler will replace it */
- sel->cdata->evaluate = sel->evaluate;
-
- /* Initialize the flags */
- sel->cdata->flags = SEL_CDATA_STATICEVAL;
- if (!(sel->flags & SEL_DYNAMIC))
- {
- sel->cdata->flags |= SEL_CDATA_STATIC;
- }
- if (sel->type == SEL_SUBEXPR)
- {
- sel->cdata->flags |= SEL_CDATA_EVALMAX;
- }
- /* Set the full evaluation flag for subexpressions that require it;
- * the subexpression has already been initialized, so we can simply
- * access its compilation flags.*/
- if (sel->type == SEL_EXPRESSION || sel->type == SEL_MODIFIER)
- {
- child = sel->child;
- while (child)
- {
- if (!(child->flags & SEL_ATOMVAL) && child->child)
- {
- child->child->cdata->flags |= SEL_CDATA_FULLEVAL;
- }
- child = child->next;
- }
- }
- else if (sel->type == SEL_ROOT && sel->child->type == SEL_SUBEXPRREF)
- {
- sel->child->child->cdata->flags |= SEL_CDATA_FULLEVAL;
- }
-
- /* Initialize children */
- if (sel->type != SEL_SUBEXPRREF)
- {
- child = sel->child;
- while (child)
- {
- init_item_compilerdata(child);
- child = child->next;
- }
- }
-
- /* Determine whether we should evaluate the minimum or the maximum
- * for the children of this element. */
- if (sel->type == SEL_BOOLEAN)
- {
- gmx_bool bEvalMax;
-
- bEvalMax = (sel->u.boolt == BOOL_AND);
- child = sel->child;
- while (child)
- {
- if (bEvalMax)
- {
- child->cdata->flags |= SEL_CDATA_EVALMAX;
- }
- else if (child->type == SEL_BOOLEAN && child->u.boolt == BOOL_NOT)
- {
- child->child->cdata->flags |= SEL_CDATA_EVALMAX;
- }
- child = child->next;
- }
- }
- else if (sel->type == SEL_EXPRESSION || sel->type == SEL_MODIFIER
- || sel->type == SEL_SUBEXPR)
- {
- child = sel->child;
- while (child)
- {
- child->cdata->flags |= SEL_CDATA_EVALMAX;
- child = child->next;
- }
- }
-}
-
-/*! \brief
- * Initializes the static evaluation flag for a selection subtree.
- *
- * \param[in,out] sel Root of the selection subtree to process.
- *
- * Sets the \c bStaticEval in the compiler data structure:
- * for any element for which the evaluation group may depend on the trajectory
- * frame, the flag is cleared.
- *
- * reorder_gmx_boolean_static_children() should have been called.
- */
-static void
-init_item_staticeval(t_selelem *sel)
-{
- t_selelem *child;
-
- /* Subexpressions with full evaluation should always have bStaticEval,
- * so don't do anything if a reference to them is encountered. */
- if (sel->type == SEL_SUBEXPRREF
- && (sel->child->cdata->flags & SEL_CDATA_FULLEVAL))
- {
- return;
- }
-
- /* Propagate the bStaticEval flag to children if it is not set */
- if (!(sel->cdata->flags & SEL_CDATA_STATICEVAL))
- {
- child = sel->child;
- while (child)
- {
- if ((sel->type != SEL_EXPRESSION && sel->type != SEL_MODIFIER)
- || (child->flags & SEL_ATOMVAL))
- {
- if (child->cdata->flags & SEL_CDATA_STATICEVAL)
- {
- child->cdata->flags &= ~SEL_CDATA_STATICEVAL;
- init_item_staticeval(child);
- }
- }
- child = child->next;
- }
- }
- else /* bStaticEval is set */
- {
- /* For gmx_boolean expressions, any expression after the first dynamic
- * expression should not have bStaticEval. */
- if (sel->type == SEL_BOOLEAN)
- {
- child = sel->child;
- while (child && !(child->flags & SEL_DYNAMIC))
- {
- child = child->next;
- }
- if (child)
- {
- child = child->next;
- }
- while (child)
- {
- child->cdata->flags &= ~SEL_CDATA_STATICEVAL;
- child = child->next;
- }
- }
-
- /* Process the children */
- child = sel->child;
- while (child)
- {
- init_item_staticeval(child);
- child = child->next;
- }
- }
-}
-
-/*! \brief
- * Initializes compiler flags for subexpressions.
- *
- * \param sel Root of the selection subtree to process.
- */
-static void
-init_item_subexpr_flags(t_selelem *sel)
-{
- if (sel->type == SEL_SUBEXPR)
- {
- if (sel->refcount == 2)
- {
- sel->cdata->flags |= SEL_CDATA_SIMPLESUBEXPR;
- }
- else if (!(sel->cdata->flags & SEL_CDATA_FULLEVAL))
- {
- sel->cdata->flags |= SEL_CDATA_COMMONSUBEXPR;
- }
- }
- else if (sel->type == SEL_SUBEXPRREF && sel->child->refcount == 2)
- {
- sel->cdata->flags |= SEL_CDATA_SIMPLESUBEXPR;
- }
-
- /* Process children, but only follow subexpression references if the
- * common subexpression flag needs to be propagated. */
- if (sel->type != SEL_SUBEXPRREF
- || ((sel->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
- && sel->child->refcount > 2))
- {
- t_selelem *child = sel->child;
-
- while (child)
- {
- if (!(child->cdata->flags & SEL_CDATA_COMMONSUBEXPR))
- {
- if (sel->type != SEL_EXPRESSION || (child->flags & SEL_ATOMVAL))
- {
- child->cdata->flags |=
- (sel->cdata->flags & SEL_CDATA_COMMONSUBEXPR);
- }
- init_item_subexpr_flags(child);
- }
- child = child->next;
- }
- }
-}
-
-/*! \brief
- * Initializes the gmin and gmax fields of the compiler data structure.
- *
- * \param sel Root of the selection subtree to process.
- */
-static void
-init_item_minmax_groups(t_selelem *sel)
-{
- /* Process children. */
- if (sel->type != SEL_SUBEXPRREF)
- {
- t_selelem *child;
-
- child = sel->child;
- while (child)
- {
- init_item_minmax_groups(child);
- child = child->next;
- }
- }
-
- /* Initialize the minimum and maximum evaluation groups. */
- if (sel->type != SEL_ROOT && sel->v.type != NO_VALUE)
- {
- if (sel->v.type == GROUP_VALUE
- && (sel->cdata->flags & SEL_CDATA_STATIC))
- {
- sel->cdata->gmin = sel->v.u.g;
- sel->cdata->gmax = sel->v.u.g;
- }
- else if (sel->type == SEL_SUBEXPR
- && ((sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
- || (sel->cdata->flags & SEL_CDATA_FULLEVAL)))
- {
- sel->cdata->gmin = sel->child->cdata->gmin;
- sel->cdata->gmax = sel->child->cdata->gmax;
- }
- else
- {
- sel->cdata->flags |= SEL_CDATA_MINMAXALLOC;
- snew(sel->cdata->gmin, 1);
- snew(sel->cdata->gmax, 1);
- }
- }
-}
-
-
-/********************************************************************
- * EVALUATION GROUP INITIALIZATION
- ********************************************************************/
-
-/*! \brief
- * Initializes evaluation groups for root items.
- *
- * \param[in,out] sc Selection collection data.
- *
- * The evaluation group of each \ref SEL_ROOT element corresponding to a
- * selection in \p sc is set to \p gall. The same is done for \ref SEL_ROOT
- * elements corresponding to subexpressions that need full evaluation.
- */
-static void
-initialize_evalgrps(gmx_ana_selcollection_t *sc)
-{
- t_selelem *root;
-
- root = sc->root;
- while (root)
- {
- if (root->child->type != SEL_SUBEXPR
- || (root->child->cdata->flags & SEL_CDATA_FULLEVAL))
- {
- gmx_ana_index_set(&root->u.cgrp, sc->gall.isize, sc->gall.index,
- root->u.cgrp.name, 0);
- }
- root = root->next;
- }
-}
-
-
-/********************************************************************
- * STATIC ANALYSIS
- ********************************************************************/
-
-/*! \brief
- * Marks a subtree completely dynamic or undoes such a change.
- *
- * \param sel Selection subtree to mark.
- * \param[in] bDynamic If TRUE, the \p bStatic flag of the whole
- * selection subtree is cleared. If FALSE, the flag is restored to
- * using \ref SEL_DYNAMIC.
- *
- * Does not descend into parameters of methods unless the parameters
- * are evaluated for each atom.
- */
-static void
-mark_subexpr_dynamic(t_selelem *sel, gmx_bool bDynamic)
-{
- t_selelem *child;
-
- if (!bDynamic && !(sel->flags & SEL_DYNAMIC))
- {
- sel->cdata->flags |= SEL_CDATA_STATIC;
- }
- else
- {
- sel->cdata->flags &= ~SEL_CDATA_STATIC;
- }
- child = sel->child;
- while (child)
- {
- if (sel->type != SEL_EXPRESSION || child->type != SEL_SUBEXPRREF
- || (child->u.param->flags & SPAR_ATOMVAL))
- {
- mark_subexpr_dynamic(child, bDynamic);
- }
- child = child->next;
- }
-}
-
-/*! \brief
- * Frees memory for subexpressions that are no longer needed.
- *
- * \param sel Selection subtree to check.
- *
- * Checks whether the subtree rooted at \p sel refers to any \ref SEL_SUBEXPR
- * elements that are not referred to by anything else except their own root
- * element. If such elements are found, all memory allocated for them is freed
- * except the actual element. The element is left because otherwise a dangling
- * pointer would be left at the root element, which is not traversed by this
- * function. Later compilation passes remove the stub elements.
- */
-static void
-release_subexpr_memory(t_selelem *sel)
-{
- if (sel->type == SEL_SUBEXPR)
- {
- if (sel->refcount == 2)
- {
- release_subexpr_memory(sel->child);
- sel->name = NULL;
- _gmx_selelem_free_chain(sel->child);
- _gmx_selelem_free_values(sel);
- _gmx_selelem_free_exprdata(sel);
- _gmx_selelem_free_compiler_data(sel);
- sel->child = NULL;
- }
- }
- else
- {
- t_selelem *child;
-
- child = sel->child;
- while (child)
- {
- release_subexpr_memory(child);
- child = child->next;
- }
- }
-}
-
-/*! \brief
- * Makes an evaluated selection element static.
- *
- * \param sel Selection element to make static.
- *
- * The evaluated value becomes the value of the static element.
- * The element type is changed to SEL_CONST and the children are
- * deleted.
- */
-static void
-make_static(t_selelem *sel)
-{
- /* If this is a subexpression reference and the data is stored in the
- * child, we transfer data ownership before doing anything else. */
- if (sel->type == SEL_SUBEXPRREF
- && (sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR))
- {
- if (sel->child->child->flags & SEL_ALLOCDATA)
- {
- sel->flags |= SEL_ALLOCDATA;
- sel->child->child->flags &= ~SEL_ALLOCDATA;
- }
- if (sel->child->child->flags & SEL_ALLOCVAL)
- {
- sel->flags |= SEL_ALLOCVAL;
- sel->v.nalloc = sel->child->child->v.nalloc;
- sel->child->child->flags &= ~SEL_ALLOCVAL;
- sel->child->child->v.nalloc = -1;
- }
- }
- /* When we reach here for parameter elements, the value is already
- * stored in the parent element, so make sure that it is not freed
- * through this element. */
- if (sel->type == SEL_SUBEXPRREF && sel->u.param)
- {
- sel->u.param->val.nalloc = sel->v.nalloc;
- sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- sel->v.nalloc = -1;
- }
- /* Free the children. */
- release_subexpr_memory(sel);
- _gmx_selelem_free_chain(sel->child);
- sel->child = NULL;
- /* Free the expression data as it is no longer needed */
- _gmx_selelem_free_exprdata(sel);
- /* Make the item static */
- sel->name = NULL;
- sel->type = SEL_CONST;
- sel->evaluate = NULL;
- sel->cdata->evaluate = NULL;
- /* Set the group value.
- * free_exprdata above frees the cgrp group, so we can just override it. */
- if (sel->v.type == GROUP_VALUE)
- {
- gmx_ana_index_set(&sel->u.cgrp, sel->v.u.g->isize, sel->v.u.g->index, NULL, 0);
- }
-}
-
-/*! \brief
- * Evaluates a constant expression during analyze_static().
- *
- * \param[in] data Evaluation data.
- * \param[in,out] sel Selection to process.
- * \param[in] g The evaluation group.
- * \returns 0 on success, a non-zero error code on error.
- */
-static int
-process_const(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- int rc;
-
- rc = 0;
- if (sel->v.type == GROUP_VALUE)
- {
- if (sel->cdata->evaluate)
- {
- rc = sel->cdata->evaluate(data, sel, g);
- }
- }
- /* Other constant expressions do not need evaluation */
- return rc;
-}
-
-/*! \brief
- * Sets the parameter value pointer for \ref SEL_SUBEXPRREF params.
- *
- * \param[in,out] sel Selection to process.
- *
- * Copies the value pointer of \p sel to \c sel->u.param if one is present
- * and should receive the value from the compiler
- * (most parameter values are handled during parsing).
- * If \p sel is not of type \ref SEL_SUBEXPRREF, or if \c sel->u.param is NULL,
- * the function does nothing.
- * Also, if the \c sel->u.param does not have \ref SPAR_VARNUM or
- * \ref SPAR_ATOMVAL, the function returns immediately.
- */
-static void
-store_param_val(t_selelem *sel)
-{
- /* Return immediately if there is no parameter. */
- if (sel->type != SEL_SUBEXPRREF || !sel->u.param)
- {
- return;
- }
-
- /* Or if the value does not need storing. */
- if (!(sel->u.param->flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
- {
- return;
- }
-
- if (sel->v.type == INT_VALUE || sel->v.type == REAL_VALUE
- || sel->v.type == STR_VALUE)
- {
- _gmx_selvalue_setstore(&sel->u.param->val, sel->v.u.ptr);
- }
-}
-
-/*! \brief
- * Handles the initialization of a selection method during analyze_static() pass.
- *
- * \param[in,out] sel Selection element to process.
- * \param[in] top Topology structure.
- * \param[in] isize Size of the evaluation group for the element.
- * \returns 0 on success, a non-zero error code on return.
- *
- * Calls sel_initfunc() (and possibly sel_outinitfunc()) to initialize the
- * method.
- * If no \ref SPAR_ATOMVAL parameters are present, multiple initialization
- * is prevented by using \ref SEL_METHODINIT and \ref SEL_OUTINIT flags.
- */
-static int
-init_method(t_selelem *sel, t_topology *top, int isize)
-{
- t_selelem *child;
- gmx_bool bAtomVal;
- int rc;
-
- /* Find out whether there are any atom-valued parameters */
- bAtomVal = FALSE;
- child = sel->child;
- while (child)
- {
- if (child->flags & SEL_ATOMVAL)
- {
- bAtomVal = TRUE;
- }
- child = child->next;
- }
-
- /* Initialize the method */
- if (sel->u.expr.method->init
- && (bAtomVal || !(sel->flags & SEL_METHODINIT)))
- {
- sel->flags |= SEL_METHODINIT;
- rc = sel->u.expr.method->init(top, sel->u.expr.method->nparams,
- sel->u.expr.method->param, sel->u.expr.mdata);
- if (rc != 0)
- {
- return rc;
- }
- }
- if (bAtomVal || !(sel->flags & SEL_OUTINIT))
- {
- sel->flags |= SEL_OUTINIT;
- if (sel->u.expr.method->outinit)
- {
- rc = sel->u.expr.method->outinit(top, &sel->v, sel->u.expr.mdata);
- if (rc != 0)
- {
- return rc;
- }
- if (sel->v.type != POS_VALUE && sel->v.type != GROUP_VALUE)
- {
- alloc_selection_data(sel, isize, TRUE);
- }
- }
- else
- {
- alloc_selection_data(sel, isize, TRUE);
- if ((sel->flags & SEL_DYNAMIC)
- && sel->v.type != GROUP_VALUE && sel->v.type != POS_VALUE)
- {
- sel->v.nr = isize;
- }
- /* If the method is char-valued, pre-allocate the strings. */
- if (sel->u.expr.method->flags & SMETH_CHARVAL)
- {
- int i;
-
- /* A sanity check */
- if (sel->v.type != STR_VALUE)
- {
- gmx_bug("internal error");
- return -1;
- }
- sel->flags |= SEL_ALLOCDATA;
- for (i = 0; i < isize; ++i)
- {
- if (sel->v.u.s[i] == NULL)
- {
- snew(sel->v.u.s[i], 2);
- }
- }
- }
- }
- /* Clear the values for dynamic output to avoid valgrind warnings. */
- if ((sel->flags & SEL_DYNAMIC) && sel->v.type == REAL_VALUE)
- {
- int i;
-
- for (i = 0; i < sel->v.nr; ++i)
- {
- sel->v.u.r[i] = 0.0;
- }
- }
- }
-
- return 0;
-}
-
-/*! \brief
- * Evaluates the static part of a gmx_boolean expression.
- *
- * \param[in] data Evaluation data.
- * \param[in,out] sel Boolean selection element whose children should be
- * processed.
- * \param[in] g The evaluation group.
- * \returns 0 on success, a non-zero error code on error.
- *
- * reorder_item_static_children() should have been called.
- */
-static int
-evaluate_gmx_boolean_static_part(gmx_sel_evaluate_t *data, t_selelem *sel,
- gmx_ana_index_t *g)
-{
- t_selelem *child, *next;
- int rc;
-
- /* Find the last static subexpression */
- child = sel->child;
- while (child->next && (child->next->cdata->flags & SEL_CDATA_STATIC))
- {
- child = child->next;
- }
- if (!(child->cdata->flags & SEL_CDATA_STATIC))
- {
- return 0;
- }
-
- /* Evalute the static part if there is more than one expression */
- if (child != sel->child)
- {
- next = child->next;
- child->next = NULL;
- rc = sel->cdata->evaluate(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
- /* Replace the subexpressions with the result */
- _gmx_selelem_free_chain(sel->child);
- snew(child, 1);
- child->type = SEL_CONST;
- child->flags = SEL_FLAGSSET | SEL_SINGLEVAL | SEL_ALLOCVAL | SEL_ALLOCDATA;
- _gmx_selelem_set_vtype(child, GROUP_VALUE);
- child->evaluate = NULL;
- _gmx_selvalue_reserve(&child->v, 1);
- gmx_ana_index_copy(child->v.u.g, sel->v.u.g, TRUE);
- init_item_compilerdata(child);
- init_item_minmax_groups(child);
- child->cdata->flags &= ~SEL_CDATA_STATICEVAL;
- child->cdata->flags |= sel->cdata->flags & SEL_CDATA_STATICEVAL;
- child->next = next;
- sel->child = child;
- }
- else if (child->evaluate)
- {
- rc = child->evaluate(data, child, g);
- if (rc != 0)
- {
- return rc;
- }
- }
- /* Set the evaluation function for the constant element.
- * We never need to evaluate the element again during compilation,
- * but we may need to evaluate the static part again if the
- * expression is not an OR with a static evaluation group.
- * If we reach here with a NOT expression, the NOT expression
- * is also static, and will be made a constant later, so don't waste
- * time copying the group. */
- child->evaluate = NULL;
- if (sel->u.boolt == BOOL_NOT
- || ((sel->cdata->flags & SEL_CDATA_STATICEVAL)
- && sel->u.boolt == BOOL_OR))
- {
- child->cdata->evaluate = NULL;
- }
- else
- {
- child->cdata->evaluate = &_gmx_sel_evaluate_static;
- /* The cgrp has only been allocated if it originated from an
- * external index group. In that case, we need special handling
- * to preserve the name of the group and to not leak memory.
- * If cgrp has been set in make_static(), it is not allocated,
- * and hence we can overwrite it safely. */
- if (child->u.cgrp.nalloc_index > 0)
- {
- char *name = child->u.cgrp.name;
- gmx_ana_index_copy(&child->u.cgrp, child->v.u.g, FALSE);
- gmx_ana_index_squeeze(&child->u.cgrp);
- child->u.cgrp.name = name;
- }
- else
- {
- gmx_ana_index_copy(&child->u.cgrp, child->v.u.g, TRUE);
- }
- }
- return 0;
-}
-
-/*! \brief
- * Evaluates the minimum and maximum groups for a gmx_boolean expression.
- *
- * \param[in] sel \ref SEL_BOOLEAN element currently being evaluated.
- * \param[in] g Group for which \p sel has been evaluated.
- * \param[out] gmin Largest subset of the possible values of \p sel.
- * \param[out] gmax Smallest superset of the possible values of \p sel.
- *
- * This is a helper function for analyze_static() that is called for
- * dynamic \ref SEL_BOOLEAN elements after they have been evaluated.
- * It uses the minimum and maximum groups of the children to calculate
- * the minimum and maximum groups for \p sel, and also updates the static
- * part of \p sel (which is in the first child) if the children give
- * cause for this.
- *
- * This function may allocate some extra memory for \p gmin and \p gmax,
- * but as these groups are freed at the end of analyze_static() (which is
- * reached shortly after this function returns), this should not be a major
- * problem.
- */
-static void
-evaluate_gmx_boolean_minmax_grps(t_selelem *sel, gmx_ana_index_t *g,
- gmx_ana_index_t *gmin, gmx_ana_index_t *gmax)
-{
- t_selelem *child;
-
- switch (sel->u.boolt)
- {
- case BOOL_NOT:
- gmx_ana_index_reserve(gmin, g->isize);
- gmx_ana_index_reserve(gmax, g->isize);
- gmx_ana_index_difference(gmax, g, sel->child->cdata->gmin);
- gmx_ana_index_difference(gmin, g, sel->child->cdata->gmax);
- break;
-
- case BOOL_AND:
- gmx_ana_index_copy(gmin, sel->child->cdata->gmin, TRUE);
- gmx_ana_index_copy(gmax, sel->child->cdata->gmax, TRUE);
- child = sel->child->next;
- while (child && gmax->isize > 0)
- {
- gmx_ana_index_intersection(gmin, gmin, child->cdata->gmin);
- gmx_ana_index_intersection(gmax, gmax, child->cdata->gmax);
- child = child->next;
- }
- /* Update the static part if other expressions limit it */
- if ((sel->child->cdata->flags & SEL_CDATA_STATIC)
- && sel->child->v.u.g->isize > gmax->isize)
- {
- gmx_ana_index_copy(sel->child->v.u.g, gmax, FALSE);
- gmx_ana_index_squeeze(sel->child->v.u.g);
- if (sel->child->u.cgrp.isize > 0)
- {
- gmx_ana_index_copy(&sel->child->u.cgrp, gmax, FALSE);
- gmx_ana_index_squeeze(&sel->child->u.cgrp);
- }
- }
- break;
-
- case BOOL_OR:
- /* We can assume here that the gmin of children do not overlap
- * because of the way _gmx_sel_evaluate_or() works. */
- gmx_ana_index_reserve(gmin, g->isize);
- gmx_ana_index_reserve(gmax, g->isize);
- gmx_ana_index_copy(gmin, sel->child->cdata->gmin, FALSE);
- gmx_ana_index_copy(gmax, sel->child->cdata->gmax, FALSE);
- child = sel->child->next;
- while (child && gmin->isize < g->isize)
- {
- gmx_ana_index_merge(gmin, gmin, child->cdata->gmin);
- gmx_ana_index_union(gmax, gmax, child->cdata->gmax);
- child = child->next;
- }
- /* Update the static part if other expressions have static parts
- * that are not included. */
- if ((sel->child->cdata->flags & SEL_CDATA_STATIC)
- && sel->child->v.u.g->isize < gmin->isize)
- {
- gmx_ana_index_reserve(sel->child->v.u.g, gmin->isize);
- gmx_ana_index_copy(sel->child->v.u.g, gmin, FALSE);
- if (sel->child->u.cgrp.isize > 0)
- {
- gmx_ana_index_reserve(&sel->child->u.cgrp, gmin->isize);
- gmx_ana_index_copy(&sel->child->u.cgrp, gmin, FALSE);
- }
- }
- break;
-
- case BOOL_XOR: /* Should not be reached */
- gmx_impl("xor expressions not implemented");
- break;
- }
-}
-
-/*! \brief
- * Evaluates the static parts of \p sel and analyzes the structure.
- *
- * \param[in] data Evaluation data.
- * \param[in,out] sel Selection currently being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * This function is used as the replacement for the \c t_selelem::evaluate
- * function pointer.
- * It does the single most complex task in the compiler: after all elements
- * have been processed, the \p gmin and \p gmax fields of \p t_compiler_data
- * have been properly initialized, enough memory has been allocated for
- * storing the value of each expression, and the static parts of the
- * expressions have been evaluated.
- * The above is exactly true only for elements other than subexpressions:
- * another pass is required for subexpressions that are referred to more than
- * once and whose evaluation group is not known in advance.
- */
-static int
-analyze_static(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- t_selelem *child, *next;
- gmx_bool bDoMinMax;
- int rc;
-
- if (sel->type != SEL_ROOT && g)
- {
- alloc_selection_data(sel, g->isize, FALSE);
- }
-
- bDoMinMax = (sel->cdata->flags & SEL_CDATA_MINMAXALLOC);
- if (sel->type != SEL_SUBEXPR && bDoMinMax)
- {
- gmx_ana_index_deinit(sel->cdata->gmin);
- gmx_ana_index_deinit(sel->cdata->gmax);
- }
-
- /* TODO: This switch is awfully long... */
- rc = 0;
- switch (sel->type)
- {
- case SEL_CONST:
- rc = process_const(data, sel, g);
- break;
-
- case SEL_EXPRESSION:
- case SEL_MODIFIER:
- rc = _gmx_sel_evaluate_method_params(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
- rc = init_method(sel, data->top, g->isize);
- if (rc != 0)
- {
- return rc;
- }
- if (!(sel->flags & SEL_DYNAMIC))
- {
- rc = sel->cdata->evaluate(data, sel, g);
- if (rc == 0 && (sel->cdata->flags & SEL_CDATA_STATIC))
- {
- make_static(sel);
- }
- }
- else
- {
- /* Modifiers need to be evaluated even though they process
- * positions to get the modified output groups from the
- * maximum possible selections. */
- if (sel->type == SEL_MODIFIER)
- {
- rc = sel->cdata->evaluate(data, sel, g);
- }
- if (bDoMinMax)
- {
- gmx_ana_index_copy(sel->cdata->gmax, g, TRUE);
- }
- }
- break;
-
- case SEL_BOOLEAN:
- if (!(sel->flags & SEL_DYNAMIC))
- {
- rc = sel->cdata->evaluate(data, sel, g);
- if (rc == 0 && (sel->cdata->flags & SEL_CDATA_STATIC))
- {
- make_static(sel);
- }
- }
- else
- {
- /* Evalute the static part if there is more than one expression */
- rc = evaluate_gmx_boolean_static_part(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
-
- /* Evaluate the selection.
- * If the type is gmx_boolean, we must explicitly handle the
- * static part evaluated in evaluate_gmx_boolean_static_part()
- * here because g may be larger. */
- if (sel->u.boolt == BOOL_AND && sel->child->type == SEL_CONST)
- {
- rc = sel->cdata->evaluate(data, sel, sel->child->v.u.g);
- }
- else
- {
- rc = sel->cdata->evaluate(data, sel, g);
- }
- if (rc != 0)
- {
- return rc;
- }
-
- /* Evaluate minimal and maximal selections */
- evaluate_gmx_boolean_minmax_grps(sel, g, sel->cdata->gmin,
- sel->cdata->gmax);
- }
- break;
-
- case SEL_ARITHMETIC:
- rc = sel->cdata->evaluate(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
- if (!(sel->flags & SEL_DYNAMIC))
- {
- if (sel->cdata->flags & SEL_CDATA_STATIC)
- {
- make_static(sel);
- }
- }
- else if (bDoMinMax)
- {
- gmx_ana_index_copy(sel->cdata->gmax, g, TRUE);
- }
- break;
-
- case SEL_ROOT:
- rc = sel->cdata->evaluate(data, sel, g);
- break;
-
- case SEL_SUBEXPR:
- if (sel->cdata->flags & (SEL_CDATA_SIMPLESUBEXPR | SEL_CDATA_FULLEVAL))
- {
- rc = sel->cdata->evaluate(data, sel, g);
- _gmx_selvalue_setstore(&sel->v, sel->child->v.u.ptr);
- }
- else if (sel->u.cgrp.isize == 0)
- {
- gmx_ana_index_reserve(&sel->u.cgrp, g->isize);
- rc = sel->cdata->evaluate(data, sel, g);
- if (bDoMinMax)
- {
- gmx_ana_index_copy(sel->cdata->gmin, sel->child->cdata->gmin, TRUE);
- gmx_ana_index_copy(sel->cdata->gmax, sel->child->cdata->gmax, TRUE);
- }
- }
- else
- {
- int isize = gmx_ana_index_difference_size(g, &sel->u.cgrp);
- if (isize > 0)
- {
- isize += sel->u.cgrp.isize;
- gmx_ana_index_reserve(&sel->u.cgrp, isize);
- alloc_selection_data(sel, isize, FALSE);
- }
- rc = sel->cdata->evaluate(data, sel, g);
- if (isize > 0 && bDoMinMax)
- {
- gmx_ana_index_reserve(sel->cdata->gmin,
- sel->cdata->gmin->isize
- + sel->child->cdata->gmin->isize);
- gmx_ana_index_reserve(sel->cdata->gmax,
- sel->cdata->gmax->isize
- + sel->child->cdata->gmax->isize);
- gmx_ana_index_merge(sel->cdata->gmin, sel->cdata->gmin,
- sel->child->cdata->gmin);
- gmx_ana_index_merge(sel->cdata->gmax, sel->cdata->gmax,
- sel->child->cdata->gmax);
- }
- }
- break;
-
- case SEL_SUBEXPRREF:
- if (!g && !(sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR))
- {
- /* The subexpression should have been evaluated if g is NULL
- * (i.e., this is a method parameter or a direct value of a
- * selection). */
- alloc_selection_data(sel, sel->child->cdata->gmax->isize, TRUE);
- }
- rc = sel->cdata->evaluate(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
- if ((sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
- && (sel->child->child->flags & SEL_ALLOCVAL))
- {
- _gmx_selvalue_setstore(&sel->v, sel->child->child->v.u.ptr);
- }
- /* Store the parameter value if required */
- store_param_val(sel);
- if (!(sel->flags & SEL_DYNAMIC))
- {
- if (sel->cdata->flags & SEL_CDATA_STATIC)
- {
- make_static(sel);
- }
- }
- else if (bDoMinMax)
- {
- if ((sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR) || !g)
- {
- gmx_ana_index_copy(sel->cdata->gmin, sel->child->cdata->gmin, TRUE);
- gmx_ana_index_copy(sel->cdata->gmax, sel->child->cdata->gmax, TRUE);
- }
- else
- {
- gmx_ana_index_reserve(sel->cdata->gmin,
- min(g->isize, sel->child->cdata->gmin->isize));
- gmx_ana_index_reserve(sel->cdata->gmax,
- min(g->isize, sel->child->cdata->gmax->isize));
- gmx_ana_index_intersection(sel->cdata->gmin,
- sel->child->cdata->gmin, g);
- gmx_ana_index_intersection(sel->cdata->gmax,
- sel->child->cdata->gmax, g);
- }
- }
- break;
- }
- /* Exit if there was some problem */
- if (rc != 0)
- {
- return rc;
- }
-
- /* Update the minimal and maximal evaluation groups */
- if (bDoMinMax)
- {
- gmx_ana_index_squeeze(sel->cdata->gmin);
- gmx_ana_index_squeeze(sel->cdata->gmax);
- sfree(sel->cdata->gmin->name);
- sfree(sel->cdata->gmax->name);
- sel->cdata->gmin->name = NULL;
- sel->cdata->gmax->name = NULL;
- }
-
- /* Replace the result of the evaluation */
- /* This is not necessary for subexpressions or for gmx_boolean negations
- * because the evaluation function already has done it properly. */
- if (sel->v.type == GROUP_VALUE && (sel->flags & SEL_DYNAMIC)
- && sel->type != SEL_SUBEXPR
- && !(sel->type == SEL_BOOLEAN && sel->u.boolt == BOOL_NOT))
- {
- if (sel->cdata->flags & SEL_CDATA_EVALMAX)
- {
- gmx_ana_index_copy(sel->v.u.g, sel->cdata->gmax, FALSE);
- }
- else
- {
- gmx_ana_index_copy(sel->v.u.g, sel->cdata->gmin, FALSE);
- }
- }
-
- return 0;
-}
-
-
-/********************************************************************
- * EVALUATION GROUP INITIALIZATION
- ********************************************************************/
-
-/*! \brief
- * Initializes the evaluation group for a \ref SEL_ROOT element.
- *
- * \param root Root element to initialize.
- * \param[in] gall Group of all atoms.
- *
- * Checks whether it is necessary to evaluate anything through the root
- * element, and either clears the evaluation function or initializes the
- * evaluation group.
- */
-static void
-init_root_item(t_selelem *root, gmx_ana_index_t *gall)
-{
- t_selelem *expr;
- char *name;
-
- expr = root->child;
- /* Subexpressions with non-static evaluation group should not be
- * evaluated by the root, and neither should be single-reference
- * subexpressions that don't evaluate for all atoms. */
- if (expr->type == SEL_SUBEXPR
- && (!(root->child->cdata->flags & SEL_CDATA_STATICEVAL)
- || ((root->child->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
- && !(root->child->cdata->flags & SEL_CDATA_FULLEVAL))))
- {
- root->evaluate = NULL;
- if (root->cdata)
- {
- root->cdata->evaluate = NULL;
- }
- }
-
- /* Set the evaluation group */
- name = root->u.cgrp.name;
- if (root->evaluate)
- {
- /* Non-atom-valued non-group expressions don't care about the group, so
- * don't allocate any memory for it. */
- if ((expr->flags & SEL_VARNUMVAL)
- || ((expr->flags & SEL_SINGLEVAL) && expr->v.type != GROUP_VALUE))
- {
- gmx_ana_index_set(&root->u.cgrp, -1, NULL, NULL, 0);
- }
- else if (expr->cdata->gmax->isize == gall->isize)
- {
- /* Save some memory by only referring to the global group. */
- gmx_ana_index_set(&root->u.cgrp, gall->isize, gall->index, NULL, 0);
- }
- else
- {
- gmx_ana_index_copy(&root->u.cgrp, expr->cdata->gmax, TRUE);
- }
- /* For selections, store the maximum group for
- * gmx_ana_selcollection_evaluate_fin() as the value of the root
- * element (unused otherwise). */
- if (expr->type != SEL_SUBEXPR && expr->v.u.p->g)
- {
- t_selelem *child = expr;
-
- /* TODO: This code is copied from parsetree.c; it would be better
- * to have this hardcoded only in one place. */
- while (child->type == SEL_MODIFIER)
- {
- child = child->child;
- if (child->type == SEL_SUBEXPRREF)
- {
- child = child->child->child;
- }
- }
- if (child->type == SEL_SUBEXPRREF)
- {
- child = child->child->child;
- }
- if (child->child->flags & SEL_DYNAMIC)
- {
- _gmx_selelem_set_vtype(root, GROUP_VALUE);
- root->flags |= (SEL_ALLOCVAL | SEL_ALLOCDATA);
- _gmx_selvalue_reserve(&root->v, 1);
- gmx_ana_index_copy(root->v.u.g, expr->v.u.p->g, TRUE);
- }
- }
- }
- else
- {
- gmx_ana_index_clear(&root->u.cgrp);
- }
- root->u.cgrp.name = name;
-}
-
-
-/********************************************************************
- * FINAL SUBEXPRESSION OPTIMIZATION
- ********************************************************************/
-
-/*! \brief
- * Optimizes subexpression evaluation.
- *
- * \param sel Root of the selection subtree to process.
- *
- * Optimizes away some unnecessary evaluation of subexpressions that are only
- * referenced once.
- */
-static void
-postprocess_item_subexpressions(t_selelem *sel)
-{
- /* Process children. */
- if (sel->type != SEL_SUBEXPRREF)
- {
- t_selelem *child;
-
- child = sel->child;
- while (child)
- {
- postprocess_item_subexpressions(child);
- child = child->next;
- }
- }
-
- /* Replace the evaluation function of statically evaluated subexpressions
- * for which the static group was not known in advance. */
- if (sel->type == SEL_SUBEXPR && sel->refcount > 2
- && (sel->cdata->flags & SEL_CDATA_STATICEVAL)
- && !(sel->cdata->flags & SEL_CDATA_FULLEVAL))
- {
- char *name;
-
- /* We need to free memory allocated for the group, because it is no
- * longer needed (and would be lost on next call to the evaluation
- * function). But we need to preserve the name. */
- name = sel->u.cgrp.name;
- gmx_ana_index_deinit(&sel->u.cgrp);
- sel->u.cgrp.name = name;
-
- sel->evaluate = &_gmx_sel_evaluate_subexpr_staticeval;
- if (sel->cdata)
- {
- sel->cdata->evaluate = sel->evaluate;
- }
- _gmx_selelem_free_values(sel->child);
- sel->child->mempool = NULL;
- _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
- sel->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- }
-
- /* Adjust memory allocation flags for subexpressions that are used only
- * once. This is not strictly necessary, but we do it to have the memory
- * managed consistently for all types of subexpressions. */
- if (sel->type == SEL_SUBEXPRREF
- && (sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR))
- {
- if (sel->child->child->flags & SEL_ALLOCVAL)
- {
- sel->flags |= SEL_ALLOCVAL;
- sel->flags |= (sel->child->child->flags & SEL_ALLOCDATA);
- sel->v.nalloc = sel->child->child->v.nalloc;
- sel->child->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- sel->child->child->v.nalloc = -1;
- }
- }
-
- /* Do the same for subexpressions that are evaluated at once for all atoms. */
- if (sel->type == SEL_SUBEXPR
- && !(sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
- && (sel->cdata->flags & SEL_CDATA_FULLEVAL))
- {
- sel->flags |= SEL_ALLOCVAL;
- sel->flags |= (sel->child->flags & SEL_ALLOCDATA);
- sel->v.nalloc = sel->child->v.nalloc;
- sel->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
- sel->child->v.nalloc = -1;
- }
-}
-
-
-/********************************************************************
- * COM CALCULATION INITIALIZATION
- ********************************************************************/
-
-/*! \brief
- * Initializes COM/COG calculation for method expressions that require it.
- *
- * \param sel Selection subtree to process.
- * \param[in,out] pcc Position calculation collection to use.
- * \param[in] type Default position calculation type.
- * \param[in] flags Flags for default position calculation.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Searches recursively through the selection tree for dynamic
- * \ref SEL_EXPRESSION elements that define the \c gmx_ana_selmethod_t::pupdate
- * function.
- * For each such element found, position calculation is initialized
- * for the maximal evaluation group.
- * The type of the calculation is determined by \p type and \p flags.
- * No calculation is initialized if \p type equals \ref POS_ATOM and
- * the method also defines the \c gmx_ana_selmethod_t::update method.
- */
-static int
-init_item_comg(t_selelem *sel, gmx_ana_poscalc_coll_t *pcc,
- e_poscalc_t type, int flags)
-{
- t_selelem *child;
- int rc;
-
- /* Initialize COM calculation for dynamic selections now that we know the maximal evaluation group */
- if (sel->type == SEL_EXPRESSION && sel->u.expr.method
- && sel->u.expr.method->pupdate)
- {
- if (!sel->u.expr.method->update || type != POS_ATOM)
- {
- /* Create a default calculation if one does not yet exist */
- int cflags;
- cflags = 0;
- if (!(sel->cdata->flags & SEL_CDATA_STATICEVAL))
- {
- cflags |= POS_DYNAMIC;
- }
- if (!sel->u.expr.pc)
- {
- cflags |= flags;
- rc = gmx_ana_poscalc_create(&sel->u.expr.pc, pcc, type, cflags);
- if (rc != 0)
- {
- return rc;
- }
- }
- else
- {
- gmx_ana_poscalc_set_flags(sel->u.expr.pc, cflags);
- }
- gmx_ana_poscalc_set_maxindex(sel->u.expr.pc, sel->cdata->gmax);
- snew(sel->u.expr.pos, 1);
- gmx_ana_poscalc_init_pos(sel->u.expr.pc, sel->u.expr.pos);
- }
- }
-
- /* Call recursively for all children unless the children have already been processed */
- if (sel->type != SEL_SUBEXPRREF)
- {
- child = sel->child;
- while (child)
- {
- rc = init_item_comg(child, pcc, type, flags);
- if (rc != 0)
- {
- return rc;
- }
- child = child->next;
- }
- }
- return 0;
-}
-
-
-/********************************************************************
- * COMPILER DATA FREEING
- ********************************************************************/
-
-/*! \brief
- * Frees the allocated compiler data recursively.
- *
- * \param sel Root of the selection subtree to process.
- *
- * Frees the data allocated for the compilation process.
- */
-static void
-free_item_compilerdata(t_selelem *sel)
-{
- t_selelem *child;
-
- /* Free compilation data */
- _gmx_selelem_free_compiler_data(sel);
-
- /* Call recursively for all children unless the children have already been processed */
- if (sel->type != SEL_SUBEXPRREF)
- {
- child = sel->child;
- while (child)
- {
- free_item_compilerdata(child);
- child = child->next;
- }
- }
-}
-
-
-/********************************************************************
- * MASS AND CHARGE CALCULATION
- ********************************************************************/
-
-/*! \brief
- * Initializes total masses and charges for selections.
- *
- * \param[in] top Topology information.
- * \param[in] ngrps Number of elements in the \p sel array.
- * \param[in,out] sel Array of selections to update.
- * \param[in] bMaskOnly TRUE if the positions will always be calculated
- * for all atoms, i.e., the masses/charges do not change.
- */
-static void
-calculate_mass_charge(t_topology *top, int ngrps, gmx_ana_selection_t *sel[],
- gmx_bool bMaskOnly)
-{
- int g, b, i;
-
- for (g = 0; g < ngrps; ++g)
- {
- sel[g]->g = sel[g]->p.g;
- snew(sel[g]->orgm, sel[g]->p.nr);
- snew(sel[g]->orgq, sel[g]->p.nr);
- for (b = 0; b < sel[g]->p.nr; ++b)
- {
- sel[g]->orgq[b] = 0;
- if (top)
- {
- sel[g]->orgm[b] = 0;
- for (i = sel[g]->p.m.mapb.index[b]; i < sel[g]->p.m.mapb.index[b+1]; ++i)
- {
- sel[g]->orgm[b] += top->atoms.atom[sel[g]->g->index[i]].m;
- sel[g]->orgq[b] += top->atoms.atom[sel[g]->g->index[i]].q;
- }
- }
- else
- {
- sel[g]->orgm[b] = 1;
- }
- }
- if (sel[g]->bDynamic && !bMaskOnly)
- {
- snew(sel[g]->m, sel[g]->p.nr);
- snew(sel[g]->q, sel[g]->p.nr);
- for (b = 0; b < sel[g]->p.nr; ++b)
- {
- sel[g]->m[b] = sel[g]->orgm[b];
- sel[g]->q[b] = sel[g]->orgq[b];
- }
- }
- else
- {
- sel[g]->m = sel[g]->orgm;
- sel[g]->q = sel[g]->orgq;
- }
- }
-}
-
-
-/********************************************************************
- * MAIN COMPILATION FUNCTION
- ********************************************************************/
-
-/*!
- * \param[in,out] sc Selection collection to debug.
- * \param[in] bDebug If TRUE, later call to gmx_ana_selcollection_compile()
- * will print out intermediate selection trees.
- */
-void
-gmx_ana_selcollection_set_compile_debug(gmx_ana_selcollection_t *sc, gmx_bool bDebug)
-{
- sc->bDebugCompile = bDebug;
-}
-
-/*!
- * \param[in,out] sc Selection collection to be compiled.
- * \returns 0 on successful compilation, a non-zero error code on error.
- *
- * Before compilation, the selection collection should have been initialized
- * with gmx_ana_selcollection_parse_*().
- * The compiled selection collection can be passed to
- * gmx_ana_selcollection_evaluate() to evaluate the selection for a frame.
- * If an error occurs, \p sc is cleared.
- *
- * The covered fraction information in \p sc is initialized to
- * \ref CFRAC_NONE.
- */
-int
-gmx_ana_selcollection_compile(gmx_ana_selcollection_t *sc)
-{
- gmx_sel_evaluate_t evaldata;
- t_selelem *item;
- e_poscalc_t post;
- int flags;
- int rc;
-
- rc = _gmx_sel_mempool_create(&sc->mempool);
- if (rc != 0)
- {
- return rc;
- }
- _gmx_sel_evaluate_init(&evaldata, sc->mempool, &sc->gall,
- sc->top, NULL, NULL);
-
- /* Clear the symbol table because it is not possible to parse anything
- * after compilation, and variable references in the symbol table can
- * also mess up the compilation and/or become invalid.
- */
- _gmx_selcollection_clear_symtab(sc);
-
- /* Remove any unused variables. */
- sc->root = remove_unused_subexpressions(sc->root);
- /* Extract subexpressions into separate roots */
- sc->root = extract_subexpressions(sc->root);
-
- /* Initialize the evaluation callbacks and process the tree structure
- * to conform to the expectations of the callback functions. */
- /* Also, initialize and allocate the compiler data structure */
- item = sc->root;
- while (item)
- {
- /* Process gmx_boolean and arithmetic expressions. */
- optimize_gmx_boolean_expressions(item);
- reorder_gmx_boolean_static_children(item);
- if (!optimize_arithmetic_expressions(item))
- {
- /* FIXME: Clean up the collection */
- return -1;
- }
- /* Initialize evaluation function. */
- if (!init_item_evalfunc(item))
- {
- /* FIXME: Clean up the collection */
- return -1;
- }
- setup_memory_pooling(item, sc->mempool);
- /* Initialize the compiler data */
- init_item_compilerdata(item);
- init_item_staticeval(item);
- item = item->next;
- }
- /* Initialize subexpression flags and evaluation output.
- * Requires compiler flags for the full tree. */
- item = sc->root;
- while (item)
- {
- init_item_subexpr_flags(item);
- init_item_evaloutput(item);
- item = item->next;
- }
- /* Initialize minimum/maximum index groups.
- * Requires evaluation output for the full tree. */
- item = sc->root;
- while (item)
- {
- init_item_minmax_groups(item);
- item = item->next;
- }
- /* Initialize the evaluation index groups */
- initialize_evalgrps(sc);
-
- if (sc->bDebugCompile)
- {
- fprintf(stderr, "\nTree after initial compiler processing:\n");
- gmx_ana_selcollection_print_tree(stderr, sc, FALSE);
- }
-
- /* Evaluate all static parts of the selection and analyze the tree
- * to allocate enough memory to store the value of each dynamic subtree. */
- item = sc->root;
- while (item)
- {
- if (item->child->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
- {
- mark_subexpr_dynamic(item->child, TRUE);
- }
- set_evaluation_function(item, &analyze_static);
- rc = item->evaluate(&evaldata, item, NULL);
- if (rc != 0)
- {
- /* FIXME: Clean up the collection */
- return rc;
- }
- item = item->next;
- }
-
- /* At this point, static subexpressions no longer have references to them,
- * so they can be removed. */
- sc->root = remove_unused_subexpressions(sc->root);
-
- if (sc->bDebugCompile)
- {
- fprintf(stderr, "\nTree after first analysis pass:\n");
- gmx_ana_selcollection_print_tree(stderr, sc, FALSE);
- }
-
- /* Do a second pass to evaluate static parts of common subexpressions */
- item = sc->root;
- while (item)
- {
- if (item->child->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
- {
- gmx_bool bMinMax = item->child->cdata->flags & SEL_CDATA_MINMAXALLOC;
-
- mark_subexpr_dynamic(item->child, FALSE);
- item->child->u.cgrp.isize = 0;
- /* We won't clear item->child->v.u.g here, because it may
- * be static, and hence actually point to item->child->cdata->gmax,
- * which is used below. We could also check whether this is the
- * case and only clear the group otherwise, but because the value
- * is actually overwritten immediately in the evaluate call, we
- * won't, because similar problems may arise if gmax handling ever
- * changes and the check were not updated.
- * For the same reason, we clear the min/max flag so that the
- * evaluation group doesn't get messed up. */
- set_evaluation_function(item, &analyze_static);
- item->child->cdata->flags &= ~SEL_CDATA_MINMAXALLOC;
- rc = item->evaluate(&evaldata, item->child, item->child->cdata->gmax);
- if (bMinMax)
- {
- item->child->cdata->flags |= SEL_CDATA_MINMAXALLOC;
- }
- if (rc != 0)
- {
- /* FIXME: Clean up the collection */
- return rc;
- }
- }
- item = item->next;
- }
-
- /* We need a yet another pass of subexpression removal to remove static
- * subexpressions referred to by common dynamic subexpressions. */
- sc->root = remove_unused_subexpressions(sc->root);
-
- if (sc->bDebugCompile)
- {
- fprintf(stderr, "\nTree after second analysis pass:\n");
- gmx_ana_selcollection_print_tree(stderr, sc, FALSE);
- }
-
- /* Initialize evaluation groups, position calculations for methods, perform
- * some final optimization, and free the memory allocated for the
- * compilation. */
- /* By default, use whole residues/molecules. */
- flags = POS_COMPLWHOLE;
- rc = gmx_ana_poscalc_type_from_enum(sc->rpost, &post, &flags);
- if (rc != 0)
- {
- gmx_bug("invalid default reference position type");
- /* FIXME: Clean up the collection */
- return rc;
- }
- item = sc->root;
- while (item)
- {
- init_root_item(item, &sc->gall);
- postprocess_item_subexpressions(item);
- rc = init_item_comg(item, sc->pcc, post, flags);
- if (rc != 0)
- {
- /* FIXME: Clean up the collection */
- return rc;
- }
- free_item_compilerdata(item);
- item = item->next;
- }
-
- /* Allocate memory for the evaluation memory pool. */
- rc = _gmx_sel_mempool_reserve(sc->mempool, 0);
- if (rc != 0)
- {
- return rc;
- }
-
- /* Finish up by calculating total masses and charges. */
- calculate_mass_charge(sc->top, sc->nr, sc->sel, sc->bMaskOnly);
-
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in evaluate.h.
- *
- * \todo
- * One of the major bottlenecks for selection performance is that all the
- * evaluation is carried out for atoms.
- * There are several cases when the evaluation could be done for residues
- * or molecules instead, including keywords that select by residue and
- * cases where residue centers are used as reference positions.
- * Implementing this would require a mechanism for recognizing whether
- * something can be evaluated by residue/molecule instead by atom, and
- * converting selections by residue/molecule into selections by atom
- * when necessary.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <maths.h>
-#include <smalloc.h>
-#include <vec.h>
-
-#include <indexutil.h>
-#include <poscalc.h>
-#include <selection.h>
-#include <selmethod.h>
-
-#include "evaluate.h"
-#include "mempool.h"
-#include "selcollection.h"
-#include "selelem.h"
-
-/*!
- * \param[in] fp File handle to receive the output.
- * \param[in] evalfunc Function pointer to print.
- */
-void
-_gmx_sel_print_evalfunc_name(FILE *fp, sel_evalfunc evalfunc)
-{
- if (!evalfunc)
- fprintf(fp, "none");
- else if (evalfunc == &_gmx_sel_evaluate_root)
- fprintf(fp, "root");
- else if (evalfunc == &_gmx_sel_evaluate_static)
- fprintf(fp, "static");
- else if (evalfunc == &_gmx_sel_evaluate_subexpr_simple)
- fprintf(fp, "subexpr_simple");
- else if (evalfunc == &_gmx_sel_evaluate_subexpr_staticeval)
- fprintf(fp, "subexpr_staticeval");
- else if (evalfunc == &_gmx_sel_evaluate_subexpr)
- fprintf(fp, "subexpr");
- else if (evalfunc == &_gmx_sel_evaluate_subexprref_simple)
- fprintf(fp, "ref_simple");
- else if (evalfunc == &_gmx_sel_evaluate_subexprref)
- fprintf(fp, "ref");
- else if (evalfunc == &_gmx_sel_evaluate_method)
- fprintf(fp, "method");
- else if (evalfunc == &_gmx_sel_evaluate_modifier)
- fprintf(fp, "mod");
- else if (evalfunc == &_gmx_sel_evaluate_not)
- fprintf(fp, "not");
- else if (evalfunc == &_gmx_sel_evaluate_and)
- fprintf(fp, "and");
- else if (evalfunc == &_gmx_sel_evaluate_or)
- fprintf(fp, "or");
- else if (evalfunc == &_gmx_sel_evaluate_arithmetic)
- fprintf(fp, "arithmetic");
- else
- fprintf(fp, "%p", (void*)(evalfunc));
-}
-
-/*!
- * \param[out] data Evaluation data structure to initialize.
- * \param[in] mp Memory pool for intermediate evaluation values.
- * \param[in] gall Index group with all the atoms.
- * \param[in] top Topology structure for evaluation.
- * \param[in] fr New frame for evaluation.
- * \param[in] pbc New PBC information for evaluation.
- */
-void
-_gmx_sel_evaluate_init(gmx_sel_evaluate_t *data,
- gmx_sel_mempool_t *mp, gmx_ana_index_t *gall,
- t_topology *top, t_trxframe *fr, t_pbc *pbc)
-{
- data->mp = mp;
- data->gall = gall;
- data->top = top;
- data->fr = fr;
- data->pbc = pbc;
-}
-
-/*! \brief
- * Recursively initializes the flags for evaluation.
- *
- * \param[in,out] sel Selection element to clear.
- *
- * The \ref SEL_INITFRAME flag is set for \ref SEL_EXPRESSION elements whose
- * method defines the \p init_frame callback (see sel_framefunc()), and
- * cleared for other elements.
- *
- * The \ref SEL_EVALFRAME flag is cleared for all elements.
- */
-static void
-init_frame_eval(t_selelem *sel)
-{
- while (sel)
- {
- sel->flags &= ~(SEL_INITFRAME | SEL_EVALFRAME);
- if (sel->type == SEL_EXPRESSION)
- {
- if (sel->u.expr.method && sel->u.expr.method->init_frame)
- {
- sel->flags |= SEL_INITFRAME;
- }
- }
- if (sel->child && sel->type != SEL_SUBEXPRREF)
- {
- init_frame_eval(sel->child);
- }
- sel = sel->next;
- }
-}
-
-/*!
- * \param[in,out] sc The selection collection to evaluate.
- * \param[in] fr Frame for which the evaluation should be carried out.
- * \param[in] pbc PBC data, or NULL if no PBC should be used.
- * \returns 0 on successful evaluation, a non-zero error code on error.
- *
- * This functions sets the global variables for topology, frame and PBC,
- * clears some information in the selection to initialize the evaluation
- * for a new frame, and evaluates \p sel and all the selections pointed by
- * the \p next pointers of \p sel.
- *
- * This is the only function that user code should call if they want to
- * evaluate a selection for a new frame.
- */
-int
-gmx_ana_selcollection_evaluate(gmx_ana_selcollection_t *sc,
- t_trxframe *fr, t_pbc *pbc)
-{
- gmx_sel_evaluate_t data;
- t_selelem *sel;
- int g, i;
- int rc;
-
- _gmx_sel_evaluate_init(&data, sc->mempool, &sc->gall, sc->top, fr, pbc);
- init_frame_eval(sc->root);
- sel = sc->root;
- while (sel)
- {
- /* Clear the evaluation group of subexpressions */
- if (sel->child && sel->child->type == SEL_SUBEXPR)
- {
- sel->child->u.cgrp.isize = 0;
- /* Not strictly necessary, because the value will be overwritten
- * during first evaluation of the subexpression anyways, but we
- * clear the group for clarity. Note that this is _not_ done during
- * compilation because of some additional complexities involved
- * (see compiler.c), so it should not be relied upon in
- * _gmx_sel_evaluate_subexpr(). */
- if (sel->child->v.type == GROUP_VALUE)
- {
- sel->child->v.u.g->isize = 0;
- }
- }
- if (sel->evaluate)
- {
- rc = sel->evaluate(&data, sel, NULL);
- if (rc != 0)
- {
- return rc;
- }
- }
- sel = sel->next;
- }
- /* Update selection information */
- for (g = 0; g < sc->nr; ++g)
- {
- gmx_ana_selection_t *sel = sc->sel[g];
-
- if (sel->m != sel->orgm)
- {
- for (i = 0; i < sel->p.nr; ++i)
- {
- sel->m[i] = sel->orgm[sel->p.m.refid[i]];
- sel->q[i] = sel->orgq[sel->p.m.refid[i]];
- }
- }
- if (sel->bCFracDyn)
- {
- sel->cfrac = _gmx_selelem_estimate_coverfrac(sel->selelem);
- sel->avecfrac += sel->cfrac;
- }
- }
- return 0;
-}
-
-/*!
- * \param[in,out] sc The selection collection to evaluate.
- * \param[in] nframes Total number of frames.
- * \returns 0 on successful evaluation, a non-zero error code on error.
- */
-int
-gmx_ana_selcollection_evaluate_fin(gmx_ana_selcollection_t *sc, int nframes)
-{
- t_selelem *sel;
- int g;
-
- for (g = 0; g < sc->nr; ++g)
- {
- sel = sc->sel[g]->selelem;
- if (sc->sel[g]->bDynamic)
- {
- gmx_ana_index_copy(sc->sel[g]->g, sel->v.u.g, FALSE);
- sc->sel[g]->g->name = NULL;
- gmx_ana_indexmap_update(&sc->sel[g]->p.m, sc->sel[g]->g, sc->bMaskOnly);
- sc->sel[g]->p.nr = sc->sel[g]->p.m.nr;
- }
-
- if (sc->sel[g]->bCFracDyn)
- {
- sc->sel[g]->avecfrac /= nframes;
- }
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates each child of \p sel in \p g.
- */
-int
-_gmx_sel_evaluate_children(gmx_sel_evaluate_t *data, t_selelem *sel,
- gmx_ana_index_t *g)
-{
- t_selelem *child;
- int rc;
-
- child = sel->child;
- while (child)
- {
- if (child->evaluate)
- {
- rc = child->evaluate(data, child, g);
- if (rc != 0)
- {
- return rc;
- }
- }
- child = child->next;
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated
- * (not used, can be NULL).
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates the first child element in the group defined by \p sel->u.cgrp.
- * If \p sel->u.cgrp is empty, nothing is done.
- * The value of \p sel is not touched (root elements do not evaluate to
- * values).
- *
- * This function can be used as \c t_selelem::evaluate for \ref SEL_ROOT
- * elements.
- */
-int
-_gmx_sel_evaluate_root(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- int rc;
-
- if (sel->u.cgrp.isize == 0 || !sel->child->evaluate)
- {
- return 0;
- }
-
- rc = sel->child->evaluate(data, sel->child,
- sel->u.cgrp.isize < 0 ? NULL : &sel->u.cgrp);
-
- return rc;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 for success.
- *
- * Sets the value of \p sel to the intersection of \p g and \p sel->u.cgrp.
- *
- * This function can be used as \c t_selelem::evaluate for \ref SEL_CONST
- * elements with value type \ref GROUP_VALUE.
- */
-int
-_gmx_sel_evaluate_static(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- gmx_ana_index_intersection(sel->v.u.g, &sel->u.cgrp, g);
- return 0;
-}
-
-
-/*********************************************************************
- * SUBEXPRESSION EVALUATION
- *********************************************************************/
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates the child element (there should be exactly one) in \p g.
- * The compiler has taken care that the child actually stores the evaluated
- * value in the value pointer of this element.
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_SUBEXPR
- * elements that are used only once, and hence do not need full subexpression
- * handling.
- */
-int
-_gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- int rc;
-
- if (sel->child->evaluate)
- {
- rc = sel->child->evaluate(data, sel->child, g);
- if (rc != 0)
- {
- return rc;
- }
- }
- sel->v.nr = sel->child->v.nr;
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * If this is the first call for this frame, evaluates the child element
- * there should be exactly one in \p g.
- * The compiler has taken care that the child actually stores the evaluated
- * value in the value pointer of this element.
- * Assumes that \p g is persistent for the duration of the whole evaluation.
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_SUBEXPR
- * elements that have a static evaluation group, and hence do not need full
- * subexpression handling.
- */
-int
-_gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- if (sel->u.cgrp.isize == 0)
- {
- int rc;
-
- rc = sel->child->evaluate(data, sel->child, g);
- if (rc != 0)
- {
- return rc;
- }
- sel->v.nr = sel->child->v.nr;
- gmx_ana_index_set(&sel->u.cgrp, g->isize, g->index, sel->u.cgrp.name, 0);
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Finds the part of \p g for which the subexpression
- * has not yet been evaluated by comparing \p g to \p sel->u.cgrp.
- * If the part is not empty, the child expression is evaluated for this
- * part, and the results merged to the old values of the child.
- * The value of \p sel itself is undefined after the call.
- *
- * \todo
- * The call to gmx_ana_index_difference() can take quite a lot of unnecessary
- * time if the subexpression is evaluated either several times for the same
- * group or for completely distinct groups.
- * However, in the majority of cases, these situations occur when
- * _gmx_sel_evaluate_subexpr_staticeval() can be used, so this should not be a
- * major problem.
- */
-int
-_gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- gmx_ana_index_t gmiss;
- int rc;
-
- if (sel->u.cgrp.isize == 0)
- {
- char *name;
- void *old_ptr = sel->child->v.u.ptr;
- int old_nalloc = sel->child->v.nalloc;
- _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
- rc = sel->child->evaluate(data, sel->child, g);
- _gmx_selvalue_setstore_alloc(&sel->child->v, old_ptr, old_nalloc);
- if (rc != 0)
- {
- return rc;
- }
- /* We need to keep the name for the cgrp across the copy to avoid
- * problems if g has a name set. */
- name = sel->u.cgrp.name;
- gmx_ana_index_copy(&sel->u.cgrp, g, FALSE);
- sel->u.cgrp.name = name;
- gmiss.isize = 0;
- }
- else
- {
- /* We allocate some extra memory here to avoid some computation. */
- rc = _gmx_sel_mempool_alloc_group(data->mp, &gmiss, g->isize);
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_index_difference(&gmiss, g, &sel->u.cgrp);
- if (gmiss.isize == 0)
- {
- _gmx_sel_mempool_free_group(data->mp, &gmiss);
- }
- }
- if (gmiss.isize > 0)
- {
- rc = _gmx_selelem_mempool_reserve(sel->child, gmiss.isize);
- if (rc != 0)
- {
- return rc;
- }
- /* Evaluate the missing values for the child */
- rc = sel->child->evaluate(data, sel->child, &gmiss);
- if (rc != 0)
- {
- return rc;
- }
- /* Merge the missing values to the existing ones. */
- if (sel->v.type == GROUP_VALUE)
- {
- gmx_ana_index_merge(sel->v.u.g, sel->child->v.u.g, sel->v.u.g);
- }
- else
- {
- int i, j, k;
-
- i = sel->u.cgrp.isize - 1;
- j = gmiss.isize - 1;
- /* TODO: This switch is kind of ugly, but it may be difficult to
- * do this portably without C++ templates. */
- switch (sel->v.type)
- {
- case INT_VALUE:
- for (k = sel->u.cgrp.isize + gmiss.isize - 1; k >= 0; k--)
- {
- if (i < 0 || (j >= 0 && sel->u.cgrp.index[i] < gmiss.index[j]))
- {
- sel->v.u.i[k] = sel->v.u.i[j--];
- }
- else
- {
- sel->v.u.i[k] = sel->child->v.u.i[i--];
- }
- }
- break;
-
- case REAL_VALUE:
- for (k = sel->u.cgrp.isize + gmiss.isize - 1; k >= 0; k--)
- {
- if (i < 0 || (j >= 0 && sel->u.cgrp.index[i] < gmiss.index[j]))
- {
- sel->v.u.r[k] = sel->v.u.r[j--];
- }
- else
- {
- sel->v.u.r[k] = sel->child->v.u.r[i--];
- }
- }
- break;
-
- case STR_VALUE:
- for (k = sel->u.cgrp.isize + gmiss.isize - 1; k >= 0; k--)
- {
- if (i < 0 || (j >= 0 && sel->u.cgrp.index[i] < gmiss.index[j]))
- {
- sel->v.u.s[k] = sel->v.u.s[j--];
- }
- else
- {
- sel->v.u.s[k] = sel->child->v.u.s[i--];
- }
- }
- break;
-
- case POS_VALUE:
- /* TODO: Implement this */
- gmx_impl("position subexpressions not implemented properly");
- return -1;
-
- case NO_VALUE:
- case GROUP_VALUE:
- gmx_bug("internal error");
- return -1;
- }
- }
- gmx_ana_index_merge(&sel->u.cgrp, &sel->u.cgrp, &gmiss);
- _gmx_selelem_mempool_release(sel->child);
- _gmx_sel_mempool_free_group(data->mp, &gmiss);
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 for success.
- *
- * Sets the value pointers of the child and its child to point to the same
- * memory as the value pointer of this element to avoid copying, and then
- * evaluates evaluates the child.
- *
- * This function is used as \c t_selelem:evaluate for \ref SEL_SUBEXPRREF
- * elements for which the \ref SEL_SUBEXPR does not have other references.
- */
-int
-_gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- if (g)
- {
- int rc;
-
- _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
- _gmx_selvalue_setstore_alloc(&sel->child->child->v, sel->v.u.ptr,
- sel->child->child->v.nalloc);
- rc = sel->child->evaluate(data, sel->child, g);
- if (rc != 0)
- {
- return rc;
- }
- }
- sel->v.nr = sel->child->v.nr;
- if (sel->u.param)
- {
- sel->u.param->val.nr = sel->v.nr;
- if (sel->u.param->nvalptr)
- {
- *sel->u.param->nvalptr = sel->u.param->val.nr;
- }
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * If the value type is \ref POS_VALUE, the value of the child is simply
- * copied to set the value of \p sel (the child subexpression should
- * already have been evaluated by its root).
- * If the value type is something else, the child is evaluated for the
- * group \p g, and the value of the child is then copied.
- * There should be only one child element.
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_SUBEXPRREF
- * elements.
- */
-int
-_gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- t_selelem *expr;
- int i, j;
-
- if (g)
- {
- int rc;
-
- rc = sel->child->evaluate(data, sel->child, g);
- if (rc != 0)
- {
- return rc;
- }
- }
- expr = sel->child;
- switch (sel->v.type)
- {
- case INT_VALUE:
- if (!g)
- {
- sel->v.nr = expr->v.nr;
- memcpy(sel->v.u.i, expr->v.u.i, sel->v.nr*sizeof(*sel->v.u.i));
- }
- else
- {
- sel->v.nr = g->isize;
- /* Extract the values corresponding to g */
- for (i = j = 0; i < g->isize; ++i, ++j)
- {
- while (sel->child->u.cgrp.index[j] < g->index[i])
- {
- ++j;
- }
- sel->v.u.i[i] = expr->v.u.i[j];
- }
- }
- break;
-
- case REAL_VALUE:
- if (!g)
- {
- sel->v.nr = expr->v.nr;
- memcpy(sel->v.u.r, expr->v.u.r, sel->v.nr*sizeof(*sel->v.u.r));
- }
- else
- {
- sel->v.nr = g->isize;
- /* Extract the values corresponding to g */
- for (i = j = 0; i < g->isize; ++i, ++j)
- {
- while (sel->child->u.cgrp.index[j] < g->index[i])
- {
- ++j;
- }
- sel->v.u.r[i] = expr->v.u.r[j];
- }
- }
- break;
-
- case STR_VALUE:
- if (!g)
- {
- sel->v.nr = expr->v.nr;
- memcpy(sel->v.u.s, expr->v.u.s, sel->v.nr*sizeof(*sel->v.u.s));
- }
- else
- {
- sel->v.nr = g->isize;
- /* Extract the values corresponding to g */
- for (i = j = 0; i < g->isize; ++i, ++j)
- {
- while (sel->child->u.cgrp.index[j] < g->index[i])
- {
- ++j;
- }
- sel->v.u.s[i] = expr->v.u.s[j];
- }
- }
- break;
-
- case POS_VALUE:
- /* Currently, there is no need to do anything fancy here,
- * but some future extensions may need a more flexible
- * implementation. */
- gmx_ana_pos_copy(sel->v.u.p, expr->v.u.p, FALSE);
- break;
-
- case GROUP_VALUE:
- if (!g)
- {
- gmx_ana_index_copy(sel->v.u.g, expr->v.u.g, FALSE);
- }
- else
- {
- gmx_ana_index_intersection(sel->v.u.g, expr->v.u.g, g);
- }
- break;
-
- default: /* should not be reached */
- gmx_bug("invalid subexpression reference type");
- return -1;
- }
- /* Store the number of values if needed */
- if (sel->u.param)
- {
- sel->u.param->val.nr = sel->v.nr;
- if (sel->u.param->nvalptr)
- {
- *sel->u.param->nvalptr = sel->u.param->val.nr;
- }
- }
- return 0;
-}
-
-/********************************************************************
- * METHOD EXPRESSION EVALUATION
- ********************************************************************/
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates each child of a \ref SEL_EXPRESSION element.
- * The value of \p sel is not touched.
- *
- * This function is not used as \c t_selelem::evaluate,
- * but is used internally.
- */
-int
-_gmx_sel_evaluate_method_params(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- t_selelem *child;
- int rc;
-
- child = sel->child;
- while (child)
- {
- if (child->evaluate && !(child->flags & SEL_EVALFRAME))
- {
- if (child->flags & SEL_ATOMVAL)
- {
- rc = child->evaluate(data, child, g);
- }
- else
- {
- rc = child->evaluate(data, child, NULL);
- child->flags |= SEL_EVALFRAME;
- }
- if (rc != 0)
- {
- return rc;
- }
- }
- child = child->next;
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates all child selections (using _gmx_sel_evaluate_method_params())
- * to evaluate any parameter values.
- * If this is the first time this expression is evaluated for
- * the frame, sel_framefunc() callback is called if one is provided.
- * If a reference position calculation has been initialized for this element,
- * the positions are also updated, and sel_updatefunc_pos() is used to
- * evaluate the value. Otherwise, sel_updatefunc() is used.
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_EXPRESSION
- * elements.
- */
-int
-_gmx_sel_evaluate_method(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- int rc;
-
- rc = _gmx_sel_evaluate_method_params(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
- if (sel->flags & SEL_INITFRAME)
- {
- rc = sel->u.expr.method->init_frame(data->top, data->fr, data->pbc,
- sel->u.expr.mdata);
- sel->flags &= ~SEL_INITFRAME;
- if (rc != 0)
- {
- return rc;
- }
- }
- if (sel->u.expr.pc)
- {
- gmx_ana_poscalc_update(sel->u.expr.pc, sel->u.expr.pos, g,
- data->fr, data->pbc);
- rc = sel->u.expr.method->pupdate(data->top, data->fr, data->pbc,
- sel->u.expr.pos, &sel->v,
- sel->u.expr.mdata);
- }
- else
- {
- rc = sel->u.expr.method->update(data->top, data->fr, data->pbc, g,
- &sel->v, sel->u.expr.mdata);
- }
- return rc;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates all child selections (using _gmx_sel_evaluate_method_params())
- * to evaluate any parameter values.
- * If this is the first time this expression is evaluated for
- * the frame, sel_framefunc() callback is called if one is provided.
- * The modifier is then evaluated using sel_updatefunc_pos().
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_MODIFIER
- * elements.
- */
-int
-_gmx_sel_evaluate_modifier(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- int rc;
-
- rc = _gmx_sel_evaluate_method_params(data, sel, g);
- if (rc != 0)
- {
- return rc;
- }
- if (sel->flags & SEL_INITFRAME)
- {
- rc = sel->u.expr.method->init_frame(data->top, data->fr, data->pbc,
- sel->u.expr.mdata);
- sel->flags &= ~SEL_INITFRAME;
- if (rc != 0)
- {
- return rc;
- }
- }
- if (sel->child->v.type != POS_VALUE)
- {
- gmx_bug("non-position valued modifiers not implemented");
- return -1;
- }
- rc = sel->u.expr.method->pupdate(data->top, data->fr, data->pbc,
- sel->child->v.u.p,
- &sel->v, sel->u.expr.mdata);
- return rc;
-}
-
-
-/********************************************************************
- * BOOLEAN EXPRESSION EVALUATION
- ********************************************************************/
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Evaluates the child element (there should be only one) in the group
- * \p g, and then sets the value of \p sel to the complement of the
- * child value.
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_BOOLEAN
- * elements with \ref BOOL_NOT.
- */
-int
-_gmx_sel_evaluate_not(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- int rc;
-
- rc = _gmx_selelem_mempool_reserve(sel->child, g->isize);
- if (rc == 0)
- {
- rc = sel->child->evaluate(data, sel->child, g);
- }
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_index_difference(sel->v.u.g, g, sel->child->v.u.g);
- _gmx_selelem_mempool_release(sel->child);
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Short-circuiting evaluation of logical AND expressions.
- *
- * Starts by evaluating the first child element in the group \p g.
- * The each following child element is evaluated in the intersection
- * of all the previous values until all children have been evaluated
- * or the intersection becomes empty.
- * The value of \p sel is set to the intersection of all the (evaluated)
- * child values.
- *
- * If the first child does not have an evaluation function, it is skipped
- * and the evaluation is started at the second child.
- * This happens if the first child is a constant expression and during
- * compilation it was detected that the evaluation group is always a subset
- * of the constant group
- * (currently, the compiler never detects this).
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_BOOLEAN
- * elements with \ref BOOL_AND.
- */
-int
-_gmx_sel_evaluate_and(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- t_selelem *child;
- int rc;
-
- child = sel->child;
- /* Skip the first child if it does not have an evaluation function. */
- if (!child->evaluate)
- {
- child = child->next;
- }
- rc = _gmx_selelem_mempool_reserve(child, g->isize);
- if (rc == 0)
- {
- rc = child->evaluate(data, child, g);
- }
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_index_copy(sel->v.u.g, child->v.u.g, FALSE);
- _gmx_selelem_mempool_release(child);
- child = child->next;
- while (child && sel->v.u.g->isize > 0)
- {
- rc = _gmx_selelem_mempool_reserve(child, sel->v.u.g->isize);
- if (rc == 0)
- {
- rc = child->evaluate(data, child, sel->v.u.g);
- }
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_index_intersection(sel->v.u.g, sel->v.u.g, child->v.u.g);
- _gmx_selelem_mempool_release(child);
- child = child->next;
- }
- return 0;
-}
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Short-circuiting evaluation of logical OR expressions.
- *
- * Starts by evaluating the first child element in the group \p g.
- * For each subsequent child, finds the part of \p g that is not
- * included the value of any previous child, and evaluates the child
- * in that group until the last child is evaluated or all of \p g
- * is included in some child value.
- * The value of \p sel is set to the union of all the (evaluated)
- * child values.
- *
- * If the first child does not have an evaluation function, its value is
- * used without evaluation.
- * This happens if the first child is a constant expression, the selection
- * has been compiled, and the evaluation group is the same for each frame.
- * In this case, the compiler has taken care of that the child value is a
- * subset of \p g, making it unnecessary to evaluate it.
- *
- * This function is used as \c t_selelem::evaluate for \ref SEL_BOOLEAN
- * elements with \ref BOOL_OR.
- */
-int
-_gmx_sel_evaluate_or(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
-{
- t_selelem *child;
- gmx_ana_index_t tmp, tmp2;
- int rc;
-
- child = sel->child;
- if (child->evaluate)
- {
- rc = _gmx_selelem_mempool_reserve(child, g->isize);
- if (rc == 0)
- {
- rc = child->evaluate(data, child, g);
- }
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_index_partition(sel->v.u.g, &tmp, g, child->v.u.g);
- _gmx_selelem_mempool_release(child);
- }
- else
- {
- gmx_ana_index_partition(sel->v.u.g, &tmp, g, child->v.u.g);
- }
- child = child->next;
- while (child && tmp.isize > 0)
- {
- tmp.name = NULL;
- rc = _gmx_selelem_mempool_reserve(child, tmp.isize);
- if (rc == 0)
- {
- rc = child->evaluate(data, child, &tmp);
- }
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_index_partition(&tmp, &tmp2, &tmp, child->v.u.g);
- _gmx_selelem_mempool_release(child);
- sel->v.u.g->isize += tmp.isize;
- tmp.isize = tmp2.isize;
- tmp.index = tmp2.index;
- child = child->next;
- }
- gmx_ana_index_sort(sel->v.u.g);
- return 0;
-}
-
-
-/********************************************************************
- * ARITHMETIC EVALUATION
- ********************************************************************/
-
-/*!
- * \param[in] data Data for the current frame.
- * \param[in] sel Selection element being evaluated.
- * \param[in] g Group for which \p sel should be evaluated.
- * \returns 0 on success, a non-zero error code on error.
- */
-int
-_gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t *data, t_selelem *sel,
- gmx_ana_index_t *g)
-{
- t_selelem *left, *right;
- int n, i, i1, i2;
- real lval, rval=0., val=0.;
- int rc;
-
- left = sel->child;
- right = left->next;
-
- if (left->mempool)
- {
- _gmx_selvalue_setstore(&left->v, sel->v.u.ptr);
- if (right)
- {
- rc = _gmx_selelem_mempool_reserve(right, g->isize);
- if (rc != 0)
- {
- return rc;
- }
- }
- }
- else if (right && right->mempool)
- {
- _gmx_selvalue_setstore(&right->v, sel->v.u.ptr);
- }
- rc = _gmx_sel_evaluate_children(data, sel, g);
-
- n = (sel->flags & SEL_SINGLEVAL) ? 1 : g->isize;
- sel->v.nr = n;
- for (i = i1 = i2 = 0; i < n; ++i)
- {
- lval = left->v.u.r[i1];
- if (sel->u.arith.type != ARITH_NEG)
- {
- rval = right->v.u.r[i2];
- }
- switch (sel->u.arith.type)
- {
- case ARITH_PLUS: val = lval + rval; break;
- case ARITH_MINUS: val = lval - rval; break;
- case ARITH_NEG: val = -lval; break;
- case ARITH_MULT: val = lval * rval; break;
- case ARITH_DIV: val = lval / rval; break;
- case ARITH_EXP: val = pow(lval, rval); break;
- }
- sel->v.u.r[i] = val;
- if (!(left->flags & SEL_SINGLEVAL))
- {
- ++i1;
- }
- if (sel->u.arith.type != ARITH_NEG && !(right->flags & SEL_SINGLEVAL))
- {
- ++i2;
- }
- }
-
- if (left->mempool)
- {
- _gmx_selvalue_setstore(&left->v, NULL);
- if (right)
- {
- _gmx_selelem_mempool_release(right);
- }
- }
- else if (right && right->mempool)
- {
- _gmx_selvalue_setstore(&right->v, NULL);
- }
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Evaluation functions for sel_evalfunc().
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- * Users should only use gmx_ana_selcollection_evaluate() declared in
- * \ref selection.h to evaluate selections.
- *
- * The functions defined in this header file are all the possible values
- * for the \c t_selelem::evaluate field (in addition to NULL).
- */
-#ifndef SELECTION_EVALUATE_H
-#define SELECTION_EVALUATE_H
-
-#include <typedefs.h>
-
-#include <indexutil.h>
-
-#include "selelem.h"
-
-struct gmx_sel_mempool_t;
-
-/*! \internal \brief
- * Data structure for passing information required during evaluation.
- */
-typedef struct gmx_sel_evaluate_t
-{
- /** Memory pool for intermediate values. */
- struct gmx_sel_mempool_t *mp;
- /** Index group that contains all the atoms. */
- gmx_ana_index_t *gall;
- /** Topology information. */
- t_topology *top;
- /** Current frame. */
- t_trxframe *fr;
- /** PBC data. */
- t_pbc *pbc;
-} gmx_sel_evaluate_t;
-
-/*! \name Utility functions
- */
-/*@{*/
-/** Initializes an evaluation data structure. */
-void
-_gmx_sel_evaluate_init(gmx_sel_evaluate_t *data,
- struct gmx_sel_mempool_t *mp, gmx_ana_index_t *gall,
- t_topology *top, t_trxframe *fr, t_pbc *pbc);
-/** Evaluates the children of a general selection element. */
-int
-_gmx_sel_evaluate_children(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates the children of a \ref SEL_EXPRESSION element. */
-int
-_gmx_sel_evaluate_method_params(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/*@}*/
-
-/*! \name Misc. evaluation functions
- */
-/*@{*/
-/** Evaluates a root selection element. */
-int
-_gmx_sel_evaluate_root(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a static group selection element. */
-int
-_gmx_sel_evaluate_static(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates an arithmetic expression element. */
-int
-_gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/*@}*/
-
-/*! \name Subexpression evaluation functions
- */
-/*@{*/
-/** Evaluates a subexpression when there is only one reference. */
-int
-_gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a subexpression when the evaluation group is static. */
-int
-_gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a subexpression. */
-int
-_gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a subexpression reference when there are no other references. */
-int
-_gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a subexpression reference. */
-int
-_gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/*@}*/
-
-/*! \name Method evaluation functions
- */
-/*@{*/
-
-/** Evaluates a method expression. */
-int
-_gmx_sel_evaluate_method(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a modifier expression. */
-int
-_gmx_sel_evaluate_modifier(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/*@}*/
-
-/*! \name Boolean evaluation functions
- */
-/*@{*/
-/** Evaluates a boolean NOT element. */
-int
-_gmx_sel_evaluate_not(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a boolean AND element with short-circuiting. */
-int
-_gmx_sel_evaluate_and(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/** Evaluates a boolean OR element with short-circuiting. */
-int
-_gmx_sel_evaluate_or(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
-/*@}*/
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Definitions of generic keyword evaluation structures.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_KEYWORDS_H
-#define SELECTION_KEYWORDS_H
-
-struct gmx_ana_selmethod_t;
-struct t_selelem;
-struct t_selexpr_param;
-
-/** Selection method data for comparison expression evaluation. */
-extern struct gmx_ana_selmethod_t sm_compare;
-
-/** Selection method data for integer keyword evaluation. */
-extern struct gmx_ana_selmethod_t sm_keyword_int;
-/** Selection method data for real keyword evaluation. */
-extern struct gmx_ana_selmethod_t sm_keyword_real;
-/** Selection method data for string keyword evaluation. */
-extern struct gmx_ana_selmethod_t sm_keyword_str;
-/** Selection method data for position keyword evaluation. */
-extern struct gmx_ana_selmethod_t sm_keyword_pos;
-
-/** Prints information about a comparison expression. */
-void
-_gmx_selelem_print_compare_info(FILE *fp, void *data);
-
-/** Sets the position type for position keyword evaluation. */
-void
-_gmx_selelem_set_kwpos_type(struct t_selelem *sel, const char *type);
-/** Sets the flags for position keyword evaluation. */
-void
-_gmx_selelem_set_kwpos_flags(struct t_selelem *sel, int flags);
-
-/** Does custom processing for parameters of the \c same selection method. */
-int
-_gmx_selelem_custom_init_same(struct gmx_ana_selmethod_t **method,
- struct t_selexpr_param *params, void *scanner);
-
-/** Initializes a selection element for evaluating a keyword in a given group. */
-int
-_gmx_sel_init_keyword_evaluator(struct t_selelem **sel,
- struct gmx_ana_selmethod_t *method,
- struct t_selexpr_param *param, void *scanner);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Memory pooling for selection evaluation.
- *
- * \todo
- * Document these functions.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <gmx_fatal.h>
-#include <smalloc.h>
-
-#include <indexutil.h>
-
-#include "mempool.h"
-
-#define ALIGN_STEP 8
-
-typedef struct gmx_sel_mempool_block_t
-{
- void *ptr;
- size_t size;
-} gmx_sel_mempool_block_t;
-
-struct gmx_sel_mempool_t
-{
- size_t currsize;
- size_t freesize;
- char *buffer;
- char *freeptr;
- int nblocks;
- gmx_sel_mempool_block_t *blockstack;
- int blockstack_nalloc;
- size_t maxsize;
-};
-
-int
-_gmx_sel_mempool_create(gmx_sel_mempool_t **mpp)
-{
- gmx_sel_mempool_t *mp;
-
- snew(mp, 1);
- mp->currsize = 0;
- mp->freesize = 0;
- mp->buffer = NULL;
- mp->freeptr = NULL;
- mp->nblocks = 0;
- mp->blockstack = NULL;
- mp->blockstack_nalloc = 0;
- mp->maxsize = 0;
- *mpp = mp;
- return 0;
-}
-
-void
-_gmx_sel_mempool_destroy(gmx_sel_mempool_t *mp)
-{
- if (!mp->buffer)
- {
- int i;
-
- for (i = 0; i < mp->nblocks; ++i)
- {
- sfree(mp->blockstack[i].ptr);
- }
- }
- sfree(mp->buffer);
- sfree(mp->blockstack);
- sfree(mp);
-}
-
-int
-_gmx_sel_mempool_alloc(gmx_sel_mempool_t *mp, void **ptrp, size_t size)
-{
- void *ptr = NULL;
- size_t size_walign;
-
- *ptrp = NULL;
- size_walign = ((size + ALIGN_STEP - 1) / ALIGN_STEP) * ALIGN_STEP;
- if (mp->buffer)
- {
- if (mp->freesize < size)
- {
- gmx_bug("out of memory pool memory");
- return ENOMEM;
- }
- ptr = mp->freeptr;
- mp->freeptr += size_walign;
- mp->freesize -= size_walign;
- mp->currsize += size_walign;
- }
- else
- {
- ptr = malloc(size);
- if (!ptr)
- {
- gmx_mem("out of memory");
- return ENOMEM;
- }
- mp->currsize += size_walign;
- if (mp->currsize > mp->maxsize)
- {
- mp->maxsize = mp->currsize;
- }
- }
-
- if (mp->nblocks >= mp->blockstack_nalloc)
- {
- mp->blockstack_nalloc = mp->nblocks + 10;
- srenew(mp->blockstack, mp->blockstack_nalloc);
- }
- mp->blockstack[mp->nblocks].ptr = ptr;
- mp->blockstack[mp->nblocks].size = size_walign;
- mp->nblocks++;
-
- *ptrp = ptr;
- return 0;
-}
-
-void
-_gmx_sel_mempool_free(gmx_sel_mempool_t *mp, void *ptr)
-{
- int size;
-
- if (ptr == NULL)
- {
- return;
- }
- assert(mp->nblocks > 0 && mp->blockstack[mp->nblocks - 1].ptr == ptr);
- mp->nblocks--;
- size = mp->blockstack[mp->nblocks].size;
- mp->currsize -= size;
- if (mp->buffer)
- {
- mp->freeptr = (char *)ptr;
- mp->freesize += size;
- }
- else
- {
- sfree(ptr);
- }
-}
-
-int
-_gmx_sel_mempool_reserve(gmx_sel_mempool_t *mp, size_t size)
-{
- assert(mp->nblocks == 0 && !mp->buffer);
- if (size == 0)
- {
- size = mp->maxsize;
- }
- mp->buffer = (char *)malloc(size);
- if (!mp->buffer)
- {
- gmx_mem("out of memory");
- return ENOMEM;
- }
- mp->freesize = size;
- mp->freeptr = mp->buffer;
- return 0;
-}
-
-int
-_gmx_sel_mempool_alloc_group(gmx_sel_mempool_t *mp, gmx_ana_index_t *g,
- int isize)
-{
- return _gmx_sel_mempool_alloc(mp, (void **)&g->index,
- sizeof(*g->index)*isize);
-}
-
-void
-_gmx_sel_mempool_free_group(gmx_sel_mempool_t *mp, gmx_ana_index_t *g)
-{
- _gmx_sel_mempool_free(mp, g->index);
- g->index = NULL;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Declarations for memory pooling functions.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef GMX_SELECTION_MEMPOOL_H
-#define GMX_SELECTION_MEMPOOL_H
-
-struct gmx_ana_index_t;
-
-typedef struct gmx_sel_mempool_t gmx_sel_mempool_t;
-
-/** Create an empty memory pool. */
-int
-_gmx_sel_mempool_create(gmx_sel_mempool_t **mpp);
-/** Destroy a memory pool. */
-void
-_gmx_sel_mempool_destroy(gmx_sel_mempool_t *mp);
-
-/** Allocate memory from a memory pool. */
-int
-_gmx_sel_mempool_alloc(gmx_sel_mempool_t *mp, void **ptrp, size_t size);
-/** Release memory allocated from a memory pool. */
-void
-_gmx_sel_mempool_free(gmx_sel_mempool_t *mp, void *ptr);
-/** Set the size of a memory pool. */
-int
-_gmx_sel_mempool_reserve(gmx_sel_mempool_t *mp, size_t size);
-
-/** Convenience function for allocating an index group from a memory pool. */
-int
-_gmx_sel_mempool_alloc_group(gmx_sel_mempool_t *mp, struct gmx_ana_index_t *g,
- int isize);
-/** Convenience function for freeing an index group from a memory pool. */
-void
-_gmx_sel_mempool_free_group(gmx_sel_mempool_t *mp, struct gmx_ana_index_t *g);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief
- * Implementation of functions in selparam.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <smalloc.h>
-#include <string2.h>
-#include <vec.h>
-
-#include <position.h>
-#include <selmethod.h>
-#include <selparam.h>
-
-#include "parsetree.h"
-#include "position.h"
-#include "selelem.h"
-
-/*!
- * \param[in] name Name of the parameter to search.
- * \param[in] nparam Number of parameters in the \p param array.
- * \param[in] param Parameter array to search.
- * \returns Pointer to the parameter in the \p param
- * or NULL if no parameter with name \p name was found.
- *
- * The comparison is case-sensitive.
- */
-gmx_ana_selparam_t *
-gmx_ana_selparam_find(const char *name, int nparam, gmx_ana_selparam_t *param)
-{
- int i;
-
- if (nparam == 0)
- {
- return NULL;
- }
- /* Find the first non-null parameter */
- i = 0;
- while (i < nparam && param[i].name == NULL)
- {
- ++i;
- }
- /* Process the special case of a NULL parameter */
- if (name == NULL)
- {
- return (i == 0) ? NULL : ¶m[i-1];
- }
- for ( ; i < nparam; ++i)
- {
- if (!strcmp(param[i].name, name))
- {
- return ¶m[i];
- }
- /* Check for 'no' prefix on gmx_boolean parameters */
- if (param[i].val.type == NO_VALUE
- && strlen(name) > 2 && name[0] == 'n' && name[1] == 'o'
- && !strcmp(param[i].name, name+2))
- {
- return ¶m[i];
- }
- }
- return NULL;
-}
-
-/*! \brief
- * Does a type conversion on a \c t_selexpr_value.
- *
- * \param[in,out] value Value to convert.
- * \param[in] type Type to convert to.
- * \param[in] scanner Scanner data structure.
- * \returns 0 on success, a non-zero value on error.
- */
-static int
-convert_value(t_selexpr_value *value, e_selvalue_t type, void *scanner)
-{
- if (value->type == type || type == NO_VALUE)
- {
- return 0;
- }
- if (value->bExpr)
- {
- /* Conversion from atom selection to position using default
- * reference positions. */
- if (value->type == GROUP_VALUE && type == POS_VALUE)
- {
- value->u.expr =
- _gmx_sel_init_position(value->u.expr, NULL, scanner);
- if (value->u.expr == NULL)
- {
- return -1;
- }
- value->type = type;
- return 0;
- }
- return -1;
- }
- else
- {
- /* Integers to floating point are easy */
- if (value->type == INT_VALUE && type == REAL_VALUE)
- {
- value->u.r.r1 = (real)value->u.i.i1;
- value->u.r.r2 = (real)value->u.i.i2;
- value->type = type;
- return 0;
- }
- /* Reals that are integer-valued can also be converted */
- if (value->type == REAL_VALUE && type == INT_VALUE
- && gmx_within_tol(value->u.r.r1, (int)value->u.r.r1, GMX_REAL_EPS)
- && gmx_within_tol(value->u.r.r2, (int)value->u.r.r2, GMX_REAL_EPS))
- {
- value->u.i.i1 = (int)value->u.r.r1;
- value->u.i.i2 = (int)value->u.r.r2;
- value->type = type;
- return 0;
- }
- }
- return -1;
-}
-
-/*! \brief
- * Does a type conversion on a list of values.
- *
- * \param[in,out] values Values to convert.
- * \param[in] type Type to convert to.
- * \param[in] scanner Scanner data structure.
- * \returns 0 on success, a non-zero value on error.
- */
-static int
-convert_values(t_selexpr_value *values, e_selvalue_t type, void *scanner)
-{
- t_selexpr_value *value;
- int rc, rc1;
-
- rc = 0;
- value = values;
- while (value)
- {
- rc1 = convert_value(value, type, scanner);
- if (rc1 != 0 && rc == 0)
- {
- rc = rc1;
- }
- value = value->next;
- }
- /* FIXME: More informative error messages */
- return rc;
-}
-
-/*! \brief
- * Adds a child element for a parameter, keeping the parameter order.
- *
- * \param[in,out] root Root element to which the child is added.
- * \param[in] child Child to add.
- * \param[in] param Parameter for which this child is a value.
- *
- * Puts \p child in the child list of \p root such that the list remains
- * in the same order as the corresponding parameters.
- */
-static void
-place_child(t_selelem *root, t_selelem *child, gmx_ana_selparam_t *param)
-{
- gmx_ana_selparam_t *ps;
- int n;
-
- ps = root->u.expr.method->param;
- n = param - ps;
- /* Put the child element in the correct place */
- if (!root->child || n < root->child->u.param - ps)
- {
- child->next = root->child;
- root->child = child;
- }
- else
- {
- t_selelem *prev;
-
- prev = root->child;
- while (prev->next && prev->next->u.param - ps >= n)
- {
- prev = prev->next;
- }
- child->next = prev->next;
- prev->next = child;
- }
-}
-
-/*! \brief
- * Comparison function for sorting integer ranges.
- *
- * \param[in] a Pointer to the first range.
- * \param[in] b Pointer to the second range.
- * \returns -1, 0, or 1 depending on the relative order of \p a and \p b.
- *
- * The ranges are primarily sorted based on their starting point, and
- * secondarily based on length (longer ranges come first).
- */
-static int
-cmp_int_range(const void *a, const void *b)
-{
- if (((int *)a)[0] < ((int *)b)[0])
- {
- return -1;
- }
- if (((int *)a)[0] > ((int *)b)[0])
- {
- return 1;
- }
- if (((int *)a)[1] > ((int *)b)[1])
- {
- return -1;
- }
- return 0;
-}
-
-/*! \brief
- * Comparison function for sorting real ranges.
- *
- * \param[in] a Pointer to the first range.
- * \param[in] b Pointer to the second range.
- * \returns -1, 0, or 1 depending on the relative order of \p a and \p b.
- *
- * The ranges are primarily sorted based on their starting point, and
- * secondarily based on length (longer ranges come first).
- */
-static int
-cmp_real_range(const void *a, const void *b)
-{
- if (((real *)a)[0] < ((real *)b)[0])
- {
- return -1;
- }
- if (((real *)a)[0] > ((real *)b)[0])
- {
- return 1;
- }
- if (((real *)a)[1] > ((real *)b)[1])
- {
- return -1;
- }
- return 0;
-}
-
-/*! \brief
- * Parses the values for a parameter that takes integer or real ranges.
- *
- * \param[in] nval Number of values in \p values.
- * \param[in] values Pointer to the list of values.
- * \param param Parameter to parse.
- * \returns TRUE if the values were parsed successfully, FALSE otherwise.
- */
-static gmx_bool
-parse_values_range(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param)
-{
- t_selexpr_value *value;
- int *idata;
- real *rdata;
- int i, j, n;
-
- param->flags &= ~SPAR_DYNAMIC;
- if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE)
- {
- gmx_bug("internal error");
- return FALSE;
- }
- idata = NULL;
- rdata = NULL;
- if (param->val.type == INT_VALUE)
- {
- snew(idata, nval*2);
- }
- else
- {
- snew(rdata, nval*2);
- }
- value = values;
- i = 0;
- while (value)
- {
- if (value->bExpr)
- {
- _gmx_selparser_error("expressions not supported within range parameters");
- return FALSE;
- }
- if (value->type != param->val.type)
- {
- gmx_bug("internal error");
- return FALSE;
- }
- if (param->val.type == INT_VALUE)
- {
- /* Make sure the input range is in increasing order */
- if (value->u.i.i1 > value->u.i.i2)
- {
- int tmp = value->u.i.i1;
- value->u.i.i1 = value->u.i.i2;
- value->u.i.i2 = tmp;
- }
- /* Check if the new range overlaps or extends the previous one */
- if (i > 0 && value->u.i.i1 <= idata[i-1]+1 && value->u.i.i2 >= idata[i-2]-1)
- {
- idata[i-2] = min(idata[i-2], value->u.i.i1);
- idata[i-1] = max(idata[i-1], value->u.i.i2);
- }
- else
- {
- idata[i++] = value->u.i.i1;
- idata[i++] = value->u.i.i2;
- }
- }
- else
- {
- /* Make sure the input range is in increasing order */
- if (value->u.r.r1 > value->u.r.r2)
- {
- real tmp = value->u.r.r1;
- value->u.r.r1 = value->u.r.r2;
- value->u.r.r2 = tmp;
- }
- /* Check if the new range overlaps or extends the previous one */
- if (i > 0 && value->u.r.r1 <= rdata[i-1] && value->u.r.r2 >= rdata[i-2])
- {
- rdata[i-2] = min(rdata[i-2], value->u.r.r1);
- rdata[i-1] = max(rdata[i-1], value->u.r.r2);
- }
- else
- {
- rdata[i++] = value->u.r.r1;
- rdata[i++] = value->u.r.r2;
- }
- }
- value = value->next;
- }
- n = i/2;
- /* Sort the ranges and merge consequent ones */
- if (param->val.type == INT_VALUE)
- {
- qsort(idata, n, 2*sizeof(int), &cmp_int_range);
- for (i = j = 2; i < 2*n; i += 2)
- {
- if (idata[j-1]+1 >= idata[i])
- {
- if (idata[i+1] > idata[j-1])
- {
- idata[j-1] = idata[i+1];
- }
- }
- else
- {
- idata[j] = idata[i];
- idata[j+1] = idata[i+1];
- j += 2;
- }
- }
- }
- else
- {
- qsort(rdata, n, 2*sizeof(real), &cmp_real_range);
- for (i = j = 2; i < 2*n; i += 2)
- {
- if (rdata[j-1]+1 >= rdata[i])
- {
- if (rdata[i+1] > rdata[j-1])
- {
- rdata[j-1] = rdata[i+1];
- }
- }
- else
- {
- rdata[j] = rdata[i];
- rdata[j+1] = rdata[i+1];
- j += 2;
- }
- }
- }
- n = j/2;
- /* Store the values */
- if (param->flags & SPAR_VARNUM)
- {
- param->val.nr = n;
- if (param->val.type == INT_VALUE)
- {
- srenew(idata, j);
- _gmx_selvalue_setstore_alloc(¶m->val, idata, j);
- }
- else
- {
- srenew(rdata, j);
- _gmx_selvalue_setstore_alloc(¶m->val, rdata, j);
- }
- }
- else
- {
- if (n != param->val.nr)
- {
- _gmx_selparser_error("the value of parameter '%s' should consist of exactly one range",
- param->name);
- sfree(idata);
- sfree(rdata);
- return FALSE;
- }
- if (param->val.type == INT_VALUE)
- {
- memcpy(param->val.u.i, idata, 2*n*sizeof(int));
- sfree(idata);
- }
- else
- {
- memcpy(param->val.u.r, rdata, 2*n*sizeof(real));
- sfree(rdata);
- }
- }
- if (param->nvalptr)
- {
- *param->nvalptr = param->val.nr;
- }
- param->nvalptr = NULL;
-
- return TRUE;
-}
-
-/*! \brief
- * Parses the values for a parameter that takes a variable number of values.
- *
- * \param[in] nval Number of values in \p values.
- * \param[in] values Pointer to the list of values.
- * \param param Parameter to parse.
- * \param root Selection element to which child expressions are added.
- * \returns TRUE if the values were parsed successfully, FALSE otherwise.
- *
- * For integer ranges, the sequence of numbers from the first to second value
- * is stored, each as a separate value.
- */
-static gmx_bool
-parse_values_varnum(int nval, t_selexpr_value *values,
- gmx_ana_selparam_t *param, t_selelem *root)
-{
- t_selexpr_value *value;
- int i, j;
-
- param->flags &= ~SPAR_DYNAMIC;
- /* Update nval if there are integer ranges. */
- if (param->val.type == INT_VALUE)
- {
- value = values;
- while (value)
- {
- if (value->type == INT_VALUE && !value->bExpr)
- {
- nval += abs(value->u.i.i2 - value->u.i.i1);
- }
- value = value->next;
- }
- }
-
- /* Check that the value type is actually implemented */
- if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE
- && param->val.type != STR_VALUE && param->val.type != POS_VALUE)
- {
- gmx_bug("internal error");
- return FALSE;
- }
-
- /* Reserve appropriate amount of memory */
- if (param->val.type == POS_VALUE)
- {
- gmx_ana_pos_reserve(param->val.u.p, nval, 0);
- gmx_ana_pos_set_nr(param->val.u.p, nval);
- gmx_ana_indexmap_init(¶m->val.u.p->m, NULL, NULL, INDEX_UNKNOWN);
- }
- else
- {
- _gmx_selvalue_reserve(¶m->val, nval);
- }
-
- value = values;
- i = 0;
- while (value)
- {
- if (value->bExpr)
- {
- _gmx_selparser_error("expressions not supported within value lists");
- return FALSE;
- }
- if (value->type != param->val.type)
- {
- gmx_bug("internal error");
- return FALSE;
- }
- switch (param->val.type)
- {
- case INT_VALUE:
- if (value->u.i.i1 <= value->u.i.i2)
- {
- for (j = value->u.i.i1; j <= value->u.i.i2; ++j)
- {
- param->val.u.i[i++] = j;
- }
- }
- else
- {
- for (j = value->u.i.i1; j >= value->u.i.i2; --j)
- {
- param->val.u.i[i++] = j;
- }
- }
- break;
- case REAL_VALUE:
- if (value->u.r.r1 != value->u.r.r2)
- {
- _gmx_selparser_error("real ranges not supported for parameter '%s'", param->name);
- return FALSE;
- }
- param->val.u.r[i++] = value->u.r.r1;
- break;
- case STR_VALUE: param->val.u.s[i++] = strdup(value->u.s); break;
- case POS_VALUE: copy_rvec(value->u.x, param->val.u.p->x[i++]); break;
- default: /* Should not be reached */
- gmx_bug("internal error");
- return FALSE;
- }
- value = value->next;
- }
- param->val.nr = i;
- if (param->nvalptr)
- {
- *param->nvalptr = param->val.nr;
- }
- param->nvalptr = NULL;
- /* Create a dummy child element to store the string values.
- * This element is responsible for freeing the values, but carries no
- * other function. */
- if (param->val.type == STR_VALUE)
- {
- t_selelem *child;
-
- child = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype(child, STR_VALUE);
- child->name = param->name;
- child->flags &= ~SEL_ALLOCVAL;
- child->flags |= SEL_FLAGSSET | SEL_VARNUMVAL | SEL_ALLOCDATA;
- child->v.nr = param->val.nr;
- _gmx_selvalue_setstore(&child->v, param->val.u.s);
- /* Because the child is not group-valued, the u union is not used
- * for anything, so we can abuse it by storing the parameter value
- * as place_child() expects, but this is really ugly... */
- child->u.param = param;
- place_child(root, child, param);
- }
-
- return TRUE;
-}
-
-/*! \brief
- * Adds a new subexpression reference to a selection element.
- *
- * \param[in,out] root Root element to which the subexpression is added.
- * \param[in] param Parameter for which this expression is a value.
- * \param[in] expr Expression to add.
- * \returns The created child element.
- *
- * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child
- * list of \p root.
- * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is.
- * \ref SEL_ALLOCVAL is cleared for the returned element.
- */
-static t_selelem *
-add_child(t_selelem *root, gmx_ana_selparam_t *param, t_selelem *expr)
-{
- t_selelem *child;
- int rc;
-
- if (root->type != SEL_EXPRESSION && root->type != SEL_MODIFIER)
- {
- gmx_bug("unsupported root element for selection parameter parser");
- return NULL;
- }
- /* Create a subexpression reference element if necessary */
- if (expr->type == SEL_SUBEXPRREF)
- {
- child = expr;
- }
- else
- {
- child = _gmx_selelem_create(SEL_SUBEXPRREF);
- if (!child)
- {
- return NULL;
- }
- _gmx_selelem_set_vtype(child, expr->v.type);
- child->child = expr;
- }
- /* Setup the child element */
- child->flags &= ~SEL_ALLOCVAL;
- child->u.param = param;
- if (child->v.type != param->val.type)
- {
- _gmx_selparser_error("invalid expression value for parameter '%s'",
- param->name);
- goto on_error;
- }
- rc = _gmx_selelem_update_flags(child);
- if (rc != 0)
- {
- goto on_error;
- }
- if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC))
- {
- _gmx_selparser_error("parameter '%s' does not support dynamic values",
- param->name);
- goto on_error;
- }
- if (!(child->flags & SEL_DYNAMIC))
- {
- param->flags &= ~SPAR_DYNAMIC;
- }
- /* Put the child element in the correct place */
- place_child(root, child, param);
- return child;
-
-on_error:
- if (child != expr)
- {
- _gmx_selelem_free(child);
- }
- return NULL;
-}
-
-/*! \brief
- * Parses an expression value for a parameter that takes a variable number of values.
- *
- * \param[in] nval Number of values in \p values.
- * \param[in] values Pointer to the list of values.
- * \param param Parameter to parse.
- * \param root Selection element to which child expressions are added.
- * \returns TRUE if the values were parsed successfully, FALSE otherwise.
- */
-static gmx_bool
-parse_values_varnum_expr(int nval, t_selexpr_value *values,
- gmx_ana_selparam_t *param, t_selelem *root)
-{
- t_selexpr_value *value;
- t_selelem *child;
- t_selelem *expr;
-
- if (nval != 1 || !values->bExpr)
- {
- gmx_bug("internal error");
- return FALSE;
- }
-
- value = values;
- child = add_child(root, param, value->u.expr);
- value->u.expr = NULL;
- if (!child)
- {
- return FALSE;
- }
-
- /* Process single-valued expressions */
- /* TODO: We should also handle SEL_SINGLEVAL expressions here */
- if (child->v.type == POS_VALUE || child->v.type == GROUP_VALUE)
- {
- /* Set the value storage */
- _gmx_selvalue_setstore(&child->v, param->val.u.ptr);
- param->val.nr = 1;
- if (param->nvalptr)
- {
- *param->nvalptr = param->val.nr;
- }
- param->nvalptr = NULL;
- return TRUE;
- }
-
- if (!(child->flags & SEL_VARNUMVAL))
- {
- _gmx_selparser_error("invalid expression value for parameter '%s'",
- param->name);
- return FALSE;
- }
-
- child->flags |= SEL_ALLOCVAL;
- param->val.nr = -1;
- *param->nvalptr = param->val.nr;
- /* Rest of the initialization is done during compilation in
- * init_method(). */
-
- return TRUE;
-}
-
-/*! \brief
- * Initializes the storage of an expression value.
- *
- * \param[in,out] sel Selection element that evaluates the value.
- * \param[in] param Parameter to receive the value.
- * \param[in] i The value of \p sel evaluates the value \p i for
- * \p param.
- *
- * Initializes the data pointer of \p sel such that the result is stored
- * as the value \p i of \p param.
- * This function is used internally by parse_values_std().
- */
-static gmx_bool
-set_expr_value_store(t_selelem *sel, gmx_ana_selparam_t *param, int i)
-{
- if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL))
- {
- _gmx_selparser_error("invalid expression value for parameter '%s'",
- param->name);
- return FALSE;
- }
- switch (sel->v.type)
- {
- case INT_VALUE: sel->v.u.i = ¶m->val.u.i[i]; break;
- case REAL_VALUE: sel->v.u.r = ¶m->val.u.r[i]; break;
- case STR_VALUE: sel->v.u.s = ¶m->val.u.s[i]; break;
- case POS_VALUE: sel->v.u.p = ¶m->val.u.p[i]; break;
- case GROUP_VALUE: sel->v.u.g = ¶m->val.u.g[i]; break;
- default: /* Error */
- gmx_bug("internal error");
- return FALSE;
- }
- sel->v.nr = 1;
- sel->v.nalloc = -1;
- return TRUE;
-}
-
-/*! \brief
- * Parses the values for a parameter that takes a constant number of values.
- *
- * \param[in] nval Number of values in \p values.
- * \param[in] values Pointer to the list of values.
- * \param param Parameter to parse.
- * \param root Selection element to which child expressions are added.
- * \returns TRUE if the values were parsed successfully, FALSE otherwise.
- *
- * For integer ranges, the sequence of numbers from the first to second value
- * is stored, each as a separate value.
- */
-static gmx_bool
-parse_values_std(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param,
- t_selelem *root)
-{
- t_selexpr_value *value;
- t_selelem *child;
- int i, j;
- gmx_bool bDynamic;
-
- /* Handle atom-valued parameters */
- if (param->flags & SPAR_ATOMVAL)
- {
- if (nval > 1)
- {
- _gmx_selparser_error("extra values for parameter '%s'", param->name);
- return FALSE;
- }
- value = values;
- if (value->bExpr)
- {
- child = add_child(root, param, value->u.expr);
- value->u.expr = NULL;
- if (!child)
- {
- return FALSE;
- }
- child->flags |= SEL_ALLOCVAL;
- if (child->v.type != GROUP_VALUE && (child->flags & SEL_ATOMVAL))
- {
- /* Rest of the initialization is done during compilation in
- * init_method(). */
- /* TODO: Positions are not correctly handled */
- param->val.nr = -1;
- if (param->nvalptr)
- {
- *param->nvalptr = -1;
- }
- return TRUE;
- }
- param->flags &= ~SPAR_ATOMVAL;
- param->val.nr = 1;
- if (param->nvalptr)
- {
- *param->nvalptr = 1;
- }
- param->nvalptr = NULL;
- if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
- || param->val.type == STR_VALUE)
- {
- _gmx_selvalue_reserve(¶m->val, 1);
- }
- return set_expr_value_store(child, param, 0);
- }
- /* If we reach here, proceed with normal parameter handling */
- param->val.nr = 1;
- if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
- || param->val.type == STR_VALUE)
- {
- _gmx_selvalue_reserve(¶m->val, 1);
- }
- param->flags &= ~SPAR_ATOMVAL;
- param->flags &= ~SPAR_DYNAMIC;
- }
-
- value = values;
- i = 0;
- bDynamic = FALSE;
- while (value && i < param->val.nr)
- {
- if (value->type != param->val.type)
- {
- _gmx_selparser_error("incorrect value for parameter '%s' skipped", param->name);
- value = value->next;
- continue;
- }
- if (value->bExpr)
- {
- child = add_child(root, param, value->u.expr);
- /* Clear the expression from the value once it is stored */
- value->u.expr = NULL;
- /* Check that the expression is valid */
- if (!child)
- {
- return FALSE;
- }
- if (!set_expr_value_store(child, param, i))
- {
- return FALSE;
- }
- if (child->flags & SEL_DYNAMIC)
- {
- bDynamic = TRUE;
- }
- }
- else
- {
- /* Value is not an expression */
- switch (value->type)
- {
- case INT_VALUE:
- if (value->u.i.i1 <= value->u.i.i2)
- {
- for (j = value->u.i.i1; j <= value->u.i.i2 && i < param->val.nr; ++j)
- {
- param->val.u.i[i++] = j;
- }
- if (j != value->u.i.i2 + 1)
- {
- _gmx_selparser_error("extra values for parameter '%s' skipped", param->name);
- }
- }
- else
- {
- for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j)
- {
- param->val.u.i[i++] = j;
- }
- if (j != value->u.i.i2 - 1)
- {
- _gmx_selparser_error("extra values for parameter '%s' skipped", param->name);
- }
- }
- --i;
- break;
- case REAL_VALUE:
- if (value->u.r.r1 != value->u.r.r2)
- {
- _gmx_selparser_error("real ranges not supported for parameter '%s'", param->name);
- return FALSE;
- }
- param->val.u.r[i] = value->u.r.r1;
- break;
- case STR_VALUE:
- param->val.u.s[i] = strdup(value->u.s);
- break;
- case POS_VALUE:
- gmx_ana_pos_init_const(¶m->val.u.p[i], value->u.x);
- break;
- case NO_VALUE:
- case GROUP_VALUE:
- gmx_bug("internal error");
- return FALSE;
- }
- }
- ++i;
- value = value->next;
- }
- if (value)
- {
- _gmx_selparser_error("extra values for parameter '%s'", param->name);
- return FALSE;
- }
- if (i < param->val.nr)
- {
- _gmx_selparser_error("not enough values for parameter '%s'", param->name);
- return FALSE;
- }
- if (!bDynamic)
- {
- param->flags &= ~SPAR_DYNAMIC;
- }
- if (param->nvalptr)
- {
- *param->nvalptr = param->val.nr;
- }
- param->nvalptr = NULL;
-
- return TRUE;
-}
-
-/*! \brief
- * Parses the values for a gmx_boolean parameter.
- *
- * \param[in] name Name by which the parameter was given.
- * \param[in] nval Number of values in \p values.
- * \param[in] values Pointer to the list of values.
- * \param param Parameter to parse.
- * \returns TRUE if the values were parsed successfully, FALSE otherwise.
- */
-static gmx_bool
-parse_values_gmx_bool(const char *name, int nval, t_selexpr_value *values, gmx_ana_selparam_t *param)
-{
- gmx_bool bSetNo;
- int len;
-
- if (param->val.type != NO_VALUE)
- {
- gmx_bug("internal error");
- return FALSE;
- }
- if (nval > 1 || (values && values->type != INT_VALUE))
- {
- _gmx_selparser_error("gmx_boolean parameter '%s' takes only a yes/no/on/off/0/1 value", param->name);
- return FALSE;
- }
-
- bSetNo = FALSE;
- /* Check if the parameter name is given with a 'no' prefix */
- len = strlen(name);
- if (len > 2 && name[0] == 'n' && name[1] == 'o'
- && strncmp(name+2, param->name, len-2) == 0)
- {
- bSetNo = TRUE;
- }
- if (bSetNo && nval > 0)
- {
- _gmx_selparser_error("gmx_boolean parameter 'no%s' should not have a value", param->name);
- return FALSE;
- }
- if (values && values->u.i.i1 == 0)
- {
- bSetNo = TRUE;
- }
-
- *param->val.u.b = bSetNo ? FALSE : TRUE;
- return TRUE;
-}
-
-/*! \brief
- * Parses the values for an enumeration parameter.
- *
- * \param[in] nval Number of values in \p values.
- * \param[in] values Pointer to the list of values.
- * \param param Parameter to parse.
- * \returns TRUE if the values were parsed successfully, FALSE otherwise.
- */
-static gmx_bool
-parse_values_enum(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param)
-{
- int i, len, match;
-
- if (nval != 1)
- {
- _gmx_selparser_error("a single value is required for parameter '%s'", param->name);
- return FALSE;
- }
- if (values->type != STR_VALUE || param->val.type != STR_VALUE)
- {
- gmx_bug("internal error");
- return FALSE;
- }
- if (values->bExpr)
- {
- _gmx_selparser_error("expression value for enumerated parameter '%s' not supported", param->name);
- return FALSE;
- }
-
- len = strlen(values->u.s);
- i = 1;
- match = 0;
- while (param->val.u.s[i] != NULL)
- {
- if (strncmp(values->u.s, param->val.u.s[i], len) == 0)
- {
- /* Check if there is a duplicate match */
- if (match > 0)
- {
- _gmx_selparser_error("ambiguous value for parameter '%s'", param->name);
- return FALSE;
- }
- match = i;
- }
- ++i;
- }
- if (match == 0)
- {
- _gmx_selparser_error("invalid value for parameter '%s'", param->name);
- return FALSE;
- }
- param->val.u.s[0] = param->val.u.s[match];
- return TRUE;
-}
-
-/*! \brief
- * Replaces constant expressions with their values.
- *
- * \param[in,out] values First element in the value list to process.
- */
-static void
-convert_const_values(t_selexpr_value *values)
-{
- t_selexpr_value *val;
-
- val = values;
- while (val)
- {
- if (val->bExpr && val->u.expr->v.type != GROUP_VALUE &&
- val->u.expr->type == SEL_CONST)
- {
- t_selelem *expr = val->u.expr;
- val->bExpr = FALSE;
- switch (expr->v.type)
- {
- case INT_VALUE:
- val->u.i.i1 = val->u.i.i2 = expr->v.u.i[0];
- break;
- case REAL_VALUE:
- val->u.r.r1 = val->u.r.r2 = expr->v.u.r[0];
- break;
- case STR_VALUE:
- val->u.s = expr->v.u.s[0];
- break;
- case POS_VALUE:
- copy_rvec(expr->v.u.p->x[0], val->u.x);
- break;
- default:
- gmx_bug("internal error");
- break;
- }
- _gmx_selelem_free(expr);
- }
- val = val->next;
- }
-}
-
-/*!
- * \param pparams List of parameters from the selection parser.
- * \param[in] nparam Number of parameters in \p params.
- * \param params Array of parameters to parse.
- * \param root Selection element to which child expressions are added.
- * \param[in] scanner Scanner data structure.
- * \returns TRUE if the parameters were parsed successfully, FALSE otherwise.
- *
- * Initializes the \p params array based on the parameters in \p pparams.
- * See the documentation of \c gmx_ana_selparam_t for different options
- * available for parsing.
- *
- * The list \p pparams and any associated values are freed after the parameters
- * have been processed, no matter is there was an error or not.
- */
-gmx_bool
-_gmx_sel_parse_params(t_selexpr_param *pparams, int nparam, gmx_ana_selparam_t *params,
- t_selelem *root, void *scanner)
-{
- t_selexpr_param *pparam;
- gmx_ana_selparam_t *oparam;
- gmx_bool bOk, rc;
- int i;
-
- /* Check that the value pointers of SPAR_VARNUM parameters are NULL and
- * that they are not NULL for other parameters */
- bOk = TRUE;
- for (i = 0; i < nparam; ++i)
- {
- if (params[i].val.type != POS_VALUE && (params[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
- {
- if (params[i].val.u.ptr != NULL)
- {
- _gmx_selparser_error("warning: value pointer of parameter '%s' is not NULL\n"
- " although it should be for SPAR_VARNUM and SPAR_ATOMVAL parameters\n",
- params[i].name);
- }
- if ((params[i].flags & SPAR_VARNUM)
- && (params[i].flags & SPAR_DYNAMIC) && !params[i].nvalptr)
- {
- _gmx_selparser_error("error: nvalptr of parameter '%s' is NULL\n"
- " but both SPAR_VARNUM and SPAR_DYNAMIC are specified\n",
- params[i].name);
- bOk = FALSE;
- }
- }
- else
- {
- if (params[i].val.u.ptr == NULL)
- {
- _gmx_selparser_error("error: value pointer of parameter '%s' is NULL\n",
- params[i].name);
- bOk = FALSE;
- }
- }
- }
- if (!bOk)
- {
- _gmx_selexpr_free_params(pparams);
- return FALSE;
- }
- /* Parse the parameters */
- pparam = pparams;
- i = 0;
- while (pparam)
- {
- /* Find the parameter and make some checks */
- if (pparam->name != NULL)
- {
- i = -1;
- oparam = gmx_ana_selparam_find(pparam->name, nparam, params);
- }
- else if (i >= 0)
- {
- oparam = ¶ms[i];
- if (oparam->name != NULL)
- {
- oparam = NULL;
- _gmx_selparser_error("too many NULL parameters provided");
- bOk = FALSE;
- goto next_param;
- }
- ++i;
- }
- else
- {
- _gmx_selparser_error("all NULL parameters should appear in the beginning of the list");
- bOk = FALSE;
- pparam = pparam->next;
- continue;
- }
- if (!oparam)
- {
- _gmx_selparser_error("unknown parameter '%s' skipped", pparam->name);
- bOk = FALSE;
- goto next_param;
- }
- if (oparam->flags & SPAR_SET)
- {
- _gmx_selparser_error("parameter '%s' set multiple times, extra values skipped", pparam->name);
- bOk = FALSE;
- goto next_param;
- }
- oparam->flags |= SPAR_SET;
- /* Process the values for the parameter */
- convert_const_values(pparam->value);
- if (convert_values(pparam->value, oparam->val.type, scanner) != 0)
- {
- _gmx_selparser_error("invalid value for parameter '%s'", pparam->name);
- bOk = FALSE;
- goto next_param;
- }
- if (oparam->val.type == NO_VALUE)
- {
- rc = parse_values_gmx_bool(pparam->name, pparam->nval, pparam->value, oparam);
- }
- else if (oparam->flags & SPAR_RANGES)
- {
- rc = parse_values_range(pparam->nval, pparam->value, oparam);
- }
- else if (oparam->flags & SPAR_VARNUM)
- {
- if (pparam->nval == 1 && pparam->value->bExpr)
- {
- rc = parse_values_varnum_expr(pparam->nval, pparam->value, oparam, root);
- }
- else
- {
- rc = parse_values_varnum(pparam->nval, pparam->value, oparam, root);
- }
- }
- else if (oparam->flags & SPAR_ENUMVAL)
- {
- rc = parse_values_enum(pparam->nval, pparam->value, oparam);
- }
- else
- {
- rc = parse_values_std(pparam->nval, pparam->value, oparam, root);
- }
- if (!rc)
- {
- bOk = FALSE;
- }
- /* Advance to the next parameter */
-next_param:
- pparam = pparam->next;
- }
- /* Check that all required parameters are present */
- for (i = 0; i < nparam; ++i)
- {
- if (!(params[i].flags & SPAR_OPTIONAL) && !(params[i].flags & SPAR_SET))
- {
- _gmx_selparser_error("required parameter '%s' not specified", params[i].name);
- bOk = FALSE;
- }
- }
-
- _gmx_selexpr_free_params(pparams);
- return bOk;
-}
+++ /dev/null
-/* A Bison parser, made by GNU Bison 2.3. */
-
-/* Skeleton implementation for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
-
-/* As a special exception, you may create a larger work that contains
- part or all of the Bison parser skeleton and distribute that work
- under terms of your choice, so long as that work isn't itself a
- parser generator using the skeleton or a modified version thereof
- as a parser skeleton. Alternatively, if you modify or redistribute
- the parser skeleton itself, you may (at your option) remove this
- special exception, which will cause the skeleton and the resulting
- Bison output files to be licensed under the GNU General Public
- License without this special exception.
-
- This special exception was added by the Free Software Foundation in
- version 2.2 of Bison. */
-
-/* C LALR(1) parser skeleton written by Richard Stallman, by
- simplifying the original so-called "semantic" parser. */
-
-/* All symbols defined below should begin with yy or YY, to avoid
- infringing on user name space. This should be done even for local
- variables, as they might otherwise be expanded by user macros.
- There are some unavoidable exceptions within include files to
- define necessary library symbols; they are noted "INFRINGES ON
- USER NAME SPACE" below. */
-
-/* Identify Bison output. */
-#define YYBISON 1
-
-/* Bison version. */
-#define YYBISON_VERSION "2.3"
-
-/* Skeleton name. */
-#define YYSKELETON_NAME "yacc.c"
-
-/* Pure parsers. */
-#define YYPURE 1
-
-/* Using locations. */
-#define YYLSP_NEEDED 0
-
-/* Substitute the variable and function names. */
-#define yyparse _gmx_sel_yybparse
-#define yylex _gmx_sel_yyblex
-#define yyerror _gmx_sel_yyberror
-#define yylval _gmx_sel_yyblval
-#define yychar _gmx_sel_yybchar
-#define yydebug _gmx_sel_yybdebug
-#define yynerrs _gmx_sel_yybnerrs
-
-
-/* Tokens. */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
- /* Put the tokens into the symbol table, so that GDB and other debuggers
- know about them. */
- enum yytokentype {
- INVALID = 258,
- HELP = 259,
- HELP_TOPIC = 260,
- TOK_INT = 261,
- TOK_REAL = 262,
- STR = 263,
- IDENTIFIER = 264,
- CMD_SEP = 265,
- GROUP = 266,
- TO = 267,
- VARIABLE_NUMERIC = 268,
- VARIABLE_GROUP = 269,
- VARIABLE_POS = 270,
- KEYWORD_NUMERIC = 271,
- KEYWORD_STR = 272,
- KEYWORD_POS = 273,
- KEYWORD_GROUP = 274,
- METHOD_NUMERIC = 275,
- METHOD_GROUP = 276,
- METHOD_POS = 277,
- MODIFIER = 278,
- EMPTY_POSMOD = 279,
- PARAM = 280,
- END_OF_METHOD = 281,
- OF = 282,
- CMP_OP = 283,
- PARAM_REDUCT = 284,
- XOR = 285,
- OR = 286,
- AND = 287,
- NOT = 288,
- UNARY_NEG = 289,
- NUM_REDUCT = 290
- };
-#endif
-/* Tokens. */
-#define INVALID 258
-#define HELP 259
-#define HELP_TOPIC 260
-#define TOK_INT 261
-#define TOK_REAL 262
-#define STR 263
-#define IDENTIFIER 264
-#define CMD_SEP 265
-#define GROUP 266
-#define TO 267
-#define VARIABLE_NUMERIC 268
-#define VARIABLE_GROUP 269
-#define VARIABLE_POS 270
-#define KEYWORD_NUMERIC 271
-#define KEYWORD_STR 272
-#define KEYWORD_POS 273
-#define KEYWORD_GROUP 274
-#define METHOD_NUMERIC 275
-#define METHOD_GROUP 276
-#define METHOD_POS 277
-#define MODIFIER 278
-#define EMPTY_POSMOD 279
-#define PARAM 280
-#define END_OF_METHOD 281
-#define OF 282
-#define CMP_OP 283
-#define PARAM_REDUCT 284
-#define XOR 285
-#define OR 286
-#define AND 287
-#define NOT 288
-#define UNARY_NEG 289
-#define NUM_REDUCT 290
-
-
-
-
-/* Copy the first part of user declarations. */
-#line 34 "parser.y"
-
-/*! \internal \file parser.c
- * \brief Generated (from parser.y by Bison) parser for the selection language.
- */
-/*! \internal \file parser.h
- * \brief Generated (from parser.y by Bison) parser include file.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-
-#include <string2.h>
-
-#include "parsetree.h"
-#include "selelem.h"
-
-#include "scanner.h"
-
-static t_selexpr_value *
-process_value_list(t_selexpr_value *values, int *nr);
-static t_selexpr_param *
-process_param_list(t_selexpr_param *params);
-
-static void
-yyerror(yyscan_t, char const *s);
-
-
-/* Enabling traces. */
-#ifndef YYDEBUG
-# define YYDEBUG 1
-#endif
-
-/* Enabling verbose error messages. */
-#ifdef YYERROR_VERBOSE
-# undef YYERROR_VERBOSE
-# define YYERROR_VERBOSE 1
-#else
-# define YYERROR_VERBOSE 0
-#endif
-
-/* Enabling the token table. */
-#ifndef YYTOKEN_TABLE
-# define YYTOKEN_TABLE 0
-#endif
-
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-typedef union YYSTYPE
-#line 62 "parser.y"
-{
- int i;
- real r;
- char *str;
- struct gmx_ana_selmethod_t *meth;
-
- struct t_selelem *sel;
-
- struct t_selexpr_value *val;
- struct t_selexpr_param *param;
-}
-/* Line 187 of yacc.c. */
-#line 214 "parser.c"
- YYSTYPE;
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
-#endif
-
-
-
-/* Copy the second part of user declarations. */
-
-
-/* Line 216 of yacc.c. */
-#line 227 "parser.c"
-
-#ifdef short
-# undef short
-#endif
-
-#ifdef YYTYPE_UINT8
-typedef YYTYPE_UINT8 yytype_uint8;
-#else
-typedef unsigned char yytype_uint8;
-#endif
-
-#ifdef YYTYPE_INT8
-typedef YYTYPE_INT8 yytype_int8;
-#elif (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-typedef signed char yytype_int8;
-#else
-typedef short int yytype_int8;
-#endif
-
-#ifdef YYTYPE_UINT16
-typedef YYTYPE_UINT16 yytype_uint16;
-#else
-typedef unsigned short int yytype_uint16;
-#endif
-
-#ifdef YYTYPE_INT16
-typedef YYTYPE_INT16 yytype_int16;
-#else
-typedef short int yytype_int16;
-#endif
-
-#ifndef YYSIZE_T
-# ifdef __SIZE_TYPE__
-# define YYSIZE_T __SIZE_TYPE__
-# elif defined size_t
-# define YYSIZE_T size_t
-# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
-# define YYSIZE_T size_t
-# else
-# define YYSIZE_T unsigned int
-# endif
-#endif
-
-#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
-
-#ifndef YY_
-# if YYENABLE_NLS
-# if ENABLE_NLS
-# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
-# define YY_(msgid) dgettext ("bison-runtime", msgid)
-# endif
-# endif
-# ifndef YY_
-# define YY_(msgid) msgid
-# endif
-#endif
-
-/* Suppress unused-variable warnings by "using" E. */
-#if ! defined lint || defined __GNUC__
-# define YYUSE(e) ((void) (e))
-#else
-# define YYUSE(e) /* empty */
-#endif
-
-/* Identity function, used to suppress warnings about constant conditions. */
-#ifndef lint
-# define YYID(n) (n)
-#else
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static int
-YYID (int i)
-#else
-static int
-YYID (i)
- int i;
-#endif
-{
- return i;
-}
-#endif
-
-#if ! defined yyoverflow || YYERROR_VERBOSE
-
-/* The parser invokes alloca or malloc; define the necessary symbols. */
-
-# ifdef YYSTACK_USE_ALLOCA
-# if YYSTACK_USE_ALLOCA
-# ifdef __GNUC__
-# define YYSTACK_ALLOC __builtin_alloca
-# elif defined __BUILTIN_VA_ARG_INCR
-# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
-# elif defined _AIX
-# define YYSTACK_ALLOC __alloca
-# elif defined _MSC_VER
-# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
-# define alloca _alloca
-# else
-# define YYSTACK_ALLOC alloca
-# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
-# endif
-# endif
-# endif
-# endif
-# endif
-
-# ifdef YYSTACK_ALLOC
- /* Pacify GCC's `empty if-body' warning. */
-# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
-# ifndef YYSTACK_ALLOC_MAXIMUM
- /* The OS might guarantee only one guard page at the bottom of the stack,
- and a page size can be as small as 4096 bytes. So we cannot safely
- invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
- to allow for a few compiler-allocated temporary stack slots. */
-# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
-# endif
-# else
-# define YYSTACK_ALLOC YYMALLOC
-# define YYSTACK_FREE YYFREE
-# ifndef YYSTACK_ALLOC_MAXIMUM
-# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
-# endif
-# if (defined __cplusplus && ! defined _STDLIB_H \
- && ! ((defined YYMALLOC || defined malloc) \
- && (defined YYFREE || defined free)))
-# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
-# ifndef _STDLIB_H
-# define _STDLIB_H 1
-# endif
-# endif
-# ifndef YYMALLOC
-# define YYMALLOC malloc
-# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
-# endif
-# endif
-# ifndef YYFREE
-# define YYFREE free
-# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-void free (void *); /* INFRINGES ON USER NAME SPACE */
-# endif
-# endif
-# endif
-#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
-
-
-#if (! defined yyoverflow \
- && (! defined __cplusplus \
- || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
-
-/* A type that is properly aligned for any stack member. */
-union yyalloc
-{
- yytype_int16 yyss;
- YYSTYPE yyvs;
- };
-
-/* The size of the maximum gap between one aligned stack and the next. */
-# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
-
-/* The size of an array large to enough to hold all stacks, each with
- N elements. */
-# define YYSTACK_BYTES(N) \
- ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
- + YYSTACK_GAP_MAXIMUM)
-
-/* Copy COUNT objects from FROM to TO. The source and destination do
- not overlap. */
-# ifndef YYCOPY
-# if defined __GNUC__ && 1 < __GNUC__
-# define YYCOPY(To, From, Count) \
- __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
-# else
-# define YYCOPY(To, From, Count) \
- do \
- { \
- YYSIZE_T yyi; \
- for (yyi = 0; yyi < (Count); yyi++) \
- (To)[yyi] = (From)[yyi]; \
- } \
- while (YYID (0))
-# endif
-# endif
-
-/* Relocate STACK from its old location to the new one. The
- local variables YYSIZE and YYSTACKSIZE give the old and new number of
- elements in the stack, and YYPTR gives the new location of the
- stack. Advance YYPTR to a properly aligned location for the next
- stack. */
-# define YYSTACK_RELOCATE(Stack) \
- do \
- { \
- YYSIZE_T yynewbytes; \
- YYCOPY (&yyptr->Stack, Stack, yysize); \
- Stack = &yyptr->Stack; \
- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
- yyptr += yynewbytes / sizeof (*yyptr); \
- } \
- while (YYID (0))
-
-#endif
-
-/* YYFINAL -- State number of the termination state. */
-#define YYFINAL 2
-/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 417
-
-/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 49
-/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 26
-/* YYNRULES -- Number of rules. */
-#define YYNRULES 91
-/* YYNRULES -- Number of states. */
-#define YYNSTATES 150
-
-/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
-#define YYUNDEFTOK 2
-#define YYMAXUTOK 290
-
-#define YYTRANSLATE(YYX) \
- ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
-
-/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
-static const yytype_uint8 yytranslate[] =
-{
- 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 42, 43, 36, 34, 45, 35, 2, 37, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 41, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 44, 2, 46, 39, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 47, 2, 48, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
- 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 38,
- 40
-};
-
-#if YYDEBUG
-/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
- YYRHS. */
-static const yytype_uint16 yyprhs[] =
-{
- 0, 0, 3, 4, 7, 10, 13, 14, 16, 18,
- 20, 22, 25, 29, 33, 37, 39, 41, 44, 47,
- 49, 51, 55, 59, 61, 64, 66, 69, 71, 73,
- 75, 77, 80, 84, 88, 92, 96, 99, 102, 104,
- 106, 109, 113, 117, 121, 123, 125, 128, 132, 136,
- 140, 144, 148, 151, 155, 159, 161, 164, 172, 176,
- 179, 183, 185, 187, 189, 191, 194, 195, 198, 201,
- 202, 204, 208, 210, 213, 217, 219, 223, 225, 228,
- 232, 234, 236, 238, 240, 242, 244, 246, 248, 250,
- 254, 258
-};
-
-/* YYRHS -- A `-1'-separated list of the rules' RHS. */
-static const yytype_int8 yyrhs[] =
-{
- 50, 0, -1, -1, 50, 51, -1, 52, 10, -1,
- 1, 10, -1, -1, 53, -1, 6, -1, 59, -1,
- 55, -1, 59, 55, -1, 9, 41, 60, -1, 9,
- 41, 62, -1, 9, 41, 64, -1, 4, -1, 54,
- -1, 4, 5, -1, 54, 5, -1, 64, -1, 60,
- -1, 42, 55, 43, -1, 55, 23, 65, -1, 6,
- -1, 35, 6, -1, 7, -1, 35, 7, -1, 56,
- -1, 57, -1, 8, -1, 9, -1, 33, 60, -1,
- 60, 32, 60, -1, 60, 31, 60, -1, 42, 60,
- 43, -1, 62, 28, 62, -1, 11, 59, -1, 11,
- 6, -1, 24, -1, 18, -1, 61, 19, -1, 61,
- 17, 70, -1, 61, 16, 70, -1, 61, 21, 65,
- -1, 6, -1, 7, -1, 61, 16, -1, 61, 20,
- 65, -1, 62, 34, 62, -1, 62, 35, 62, -1,
- 62, 36, 62, -1, 62, 37, 62, -1, 35, 62,
- -1, 62, 39, 62, -1, 42, 62, 43, -1, 59,
- -1, 61, 17, -1, 44, 58, 45, 58, 45, 58,
- 46, -1, 42, 64, 43, -1, 22, 65, -1, 18,
- 27, 60, -1, 14, -1, 13, -1, 15, -1, 66,
- -1, 66, 26, -1, -1, 66, 67, -1, 25, 68,
- -1, -1, 69, -1, 47, 69, 48, -1, 72, -1,
- 69, 72, -1, 69, 45, 72, -1, 71, -1, 47,
- 71, 48, -1, 73, -1, 71, 73, -1, 71, 45,
- 73, -1, 60, -1, 64, -1, 62, -1, 63, -1,
- 74, -1, 56, -1, 57, -1, 59, -1, 74, -1,
- 56, 12, 56, -1, 56, 12, 57, -1, 57, 12,
- 58, -1
-};
-
-/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
-static const yytype_uint16 yyrline[] =
-{
- 0, 182, 182, 183, 192, 193, 213, 217, 218, 227,
- 237, 239, 241, 243, 245, 251, 252, 255, 256, 260,
- 261, 266, 267, 279, 280, 284, 285, 288, 289, 292,
- 293, 301, 307, 313, 325, 329, 337, 343, 351, 352,
- 356, 361, 366, 374, 386, 393, 403, 408, 416, 418,
- 420, 422, 424, 426, 428, 435, 442, 454, 459, 463,
- 471, 482, 486, 490, 499, 501, 506, 507, 512, 519,
- 520, 521, 525, 526, 528, 533, 534, 538, 539, 541,
- 545, 547, 549, 551, 553, 557, 562, 567, 572, 576,
- 581, 586
-};
-#endif
-
-#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
-/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
- First, the terminals, then, starting at YYNTOKENS, nonterminals. */
-static const char *const yytname[] =
-{
- "$end", "error", "$undefined", "INVALID", "HELP", "HELP_TOPIC",
- "TOK_INT", "TOK_REAL", "STR", "IDENTIFIER", "CMD_SEP", "GROUP", "TO",
- "VARIABLE_NUMERIC", "VARIABLE_GROUP", "VARIABLE_POS", "KEYWORD_NUMERIC",
- "KEYWORD_STR", "KEYWORD_POS", "KEYWORD_GROUP", "METHOD_NUMERIC",
- "METHOD_GROUP", "METHOD_POS", "MODIFIER", "EMPTY_POSMOD", "PARAM",
- "END_OF_METHOD", "OF", "CMP_OP", "PARAM_REDUCT", "XOR", "OR", "AND",
- "NOT", "'+'", "'-'", "'*'", "'/'", "UNARY_NEG", "'^'", "NUM_REDUCT",
- "'='", "'('", "')'", "'['", "','", "']'", "'{'", "'}'", "$accept",
- "commands", "command", "cmd_plain", "help_request", "help_topic",
- "selection", "integer_number", "real_number", "number", "string",
- "sel_expr", "pos_mod", "num_expr", "str_expr", "pos_expr",
- "method_params", "method_param_list", "method_param", "value_list",
- "value_list_contents", "basic_value_list", "basic_value_list_contents",
- "value_item", "basic_value_item", "value_item_range", 0
-};
-#endif
-
-# ifdef YYPRINT
-/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
- token YYLEX-NUM. */
-static const yytype_uint16 yytoknum[] =
-{
- 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
- 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
- 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
- 285, 286, 287, 288, 43, 45, 42, 47, 289, 94,
- 290, 61, 40, 41, 91, 44, 93, 123, 125
-};
-# endif
-
-/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
-static const yytype_uint8 yyr1[] =
-{
- 0, 49, 50, 50, 51, 51, 52, 52, 52, 52,
- 52, 52, 52, 52, 52, 53, 53, 54, 54, 55,
- 55, 55, 55, 56, 56, 57, 57, 58, 58, 59,
- 59, 60, 60, 60, 60, 60, 60, 60, 61, 61,
- 60, 60, 60, 60, 62, 62, 62, 62, 62, 62,
- 62, 62, 62, 62, 62, 63, 63, 64, 64, 64,
- 64, 60, 62, 64, 65, 65, 66, 66, 67, 68,
- 68, 68, 69, 69, 69, 70, 70, 71, 71, 71,
- 72, 72, 72, 72, 72, 73, 73, 73, 73, 74,
- 74, 74
-};
-
-/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
-static const yytype_uint8 yyr2[] =
-{
- 0, 2, 0, 2, 2, 2, 0, 1, 1, 1,
- 1, 2, 3, 3, 3, 1, 1, 2, 2, 1,
- 1, 3, 3, 1, 2, 1, 2, 1, 1, 1,
- 1, 2, 3, 3, 3, 3, 2, 2, 1, 1,
- 2, 3, 3, 3, 1, 1, 2, 3, 3, 3,
- 3, 3, 2, 3, 3, 1, 2, 7, 3, 2,
- 3, 1, 1, 1, 1, 2, 0, 2, 2, 0,
- 1, 3, 1, 2, 3, 1, 3, 1, 2, 3,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
- 3, 3
-};
-
-/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
- STATE-NUM when YYTABLE doesn't specify something else to do. Zero
- means the default is an error. */
-static const yytype_uint8 yydefact[] =
-{
- 2, 0, 1, 0, 15, 44, 45, 29, 30, 0,
- 62, 61, 63, 39, 66, 38, 0, 0, 0, 0,
- 3, 0, 7, 16, 10, 9, 20, 0, 0, 19,
- 5, 17, 0, 37, 30, 36, 0, 59, 64, 44,
- 39, 0, 31, 0, 0, 52, 0, 20, 0, 19,
- 23, 25, 0, 27, 28, 0, 4, 18, 66, 11,
- 0, 0, 46, 0, 40, 66, 66, 0, 0, 0,
- 0, 0, 0, 0, 12, 13, 14, 60, 69, 65,
- 67, 0, 0, 46, 21, 34, 54, 58, 24, 26,
- 0, 22, 33, 32, 0, 85, 86, 87, 42, 75,
- 77, 88, 41, 47, 43, 35, 48, 49, 50, 51,
- 53, 0, 44, 45, 0, 0, 0, 0, 55, 80,
- 0, 82, 83, 81, 68, 70, 72, 84, 0, 0,
- 0, 0, 0, 78, 44, 45, 0, 56, 0, 73,
- 0, 76, 89, 90, 91, 79, 71, 74, 0, 57
-};
-
-/* YYDEFGOTO[NTERM-NUM]. */
-static const yytype_int8 yydefgoto[] =
-{
- -1, 1, 20, 21, 22, 23, 24, 95, 96, 55,
- 97, 119, 27, 28, 122, 123, 37, 38, 80, 124,
- 125, 102, 99, 126, 100, 101
-};
-
-/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
- STATE-NUM. */
-#define YYPACT_NINF -93
-static const yytype_int16 yypact[] =
-{
- -93, 155, -93, 10, 19, 26, -93, -93, -3, 73,
- -93, -93, -93, 22, -93, -93, 356, 372, 317, 11,
- -93, 79, -93, 86, 70, 317, 29, 384, 180, -93,
- -93, -93, 342, -93, -93, -93, 356, -93, 6, -93,
- -93, 356, -93, 372, -10, 57, -20, -17, 256, 54,
- -93, -93, 88, -93, -93, 55, -93, -93, -93, 70,
- 356, 356, 197, 174, -93, -93, -93, 372, 372, 372,
- 372, 372, 372, 342, 29, 180, -93, 29, 221, -93,
- -93, -17, 223, -93, -93, -93, -93, -93, -93, -93,
- 11, -93, 69, -93, 78, 90, 94, -93, -93, 244,
- -93, -93, -93, -93, -93, 267, 36, 36, 57, 57,
- 57, 54, 95, 96, 375, 303, 90, 94, -93, 29,
- 392, 267, -93, -93, -93, 263, -93, -93, 71, 35,
- 11, 11, 78, -93, 105, 106, 178, 174, 303, -93,
- 11, -93, -93, -93, -93, -93, -93, -93, 80, -93
-};
-
-/* YYPGOTO[NTERM-NUM]. */
-static const yytype_int8 yypgoto[] =
-{
- -93, -93, -93, -93, -93, -93, -13, 0, 14, -81,
- -1, 87, -4, -16, -93, 3, -36, -93, -93, -93,
- 12, 81, 39, -91, -92, -67
-};
-
-/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
- positive, shift that token. If negative, reduce the rule which
- number is the opposite. If zero, do what YYDEFACT says.
- If YYTABLE_NINF, syntax error. */
-#define YYTABLE_NINF -27
-static const yytype_int16 yytable[] =
-{
- 25, 45, 48, 58, 29, 46, 83, 133, 35, 128,
- 65, 127, 59, 44, 60, 61, 75, 50, 51, 53,
- 30, 49, 91, 84, 31, 48, 85, 82, 29, 103,
- 104, 78, 79, 54, 139, 76, -8, 133, 32, 44,
- 145, 50, 51, 7, 34, 139, 52, 147, 127, 36,
- 144, 105, 106, 107, 108, 109, 110, 48, 127, 148,
- 60, 61, 121, 44, 44, 44, 44, 44, 44, 127,
- 52, 127, 70, 71, 120, 72, 111, 118, 116, 33,
- 132, 7, 34, 141, 50, 51, 7, 34, 26, 56,
- 53, 57, 117, 58, 88, 89, 72, 87, 45, 121,
- 90, 61, 130, 42, 54, 47, 131, -23, -25, 121,
- 44, 120, 26, 52, 118, 116, 140, -24, -26, 74,
- 121, 120, 121, 77, 118, 116, 149, 136, 81, 117,
- 142, 53, 120, 129, 120, 118, 116, 118, 116, 117,
- 53, 0, 0, 98, 143, 54, 0, 92, 93, 0,
- 117, 0, 117, 0, 54, 2, 3, 0, 0, 4,
- 81, 5, 6, 7, 8, -6, 9, 0, 10, 11,
- 12, 0, 0, 13, 0, 0, 0, 14, 0, 15,
- 50, 51, 7, 34, 112, 113, 7, 34, 16, 9,
- 17, 10, 11, 12, 0, 0, 13, 18, 0, 19,
- 14, 0, 15, 50, 51, 7, 34, 0, 67, 52,
- 0, 16, 0, 114, 68, 69, 70, 71, 0, 72,
- 73, 94, 19, 138, 0, 0, 146, 112, 113, 7,
- 34, 0, 9, 0, 10, 11, 12, 0, 0, 13,
- 0, 0, 0, 14, 94, 15, 0, 0, 0, 0,
- 50, 51, 7, 34, 16, 0, 114, 68, 69, 70,
- 71, 0, 72, 73, 0, 19, 86, 0, 115, 112,
- 113, 7, 34, 0, 9, 0, 10, 11, 12, 52,
- 0, 13, 0, 0, 67, 14, 0, 15, 0, 132,
- 68, 69, 70, 71, 0, 72, 16, 0, 114, 86,
- 0, 68, 69, 70, 71, 73, 72, 19, 138, 112,
- 113, 7, 34, 0, 9, 0, 10, 11, 12, 0,
- 0, 13, 0, 39, 6, 14, 0, 15, 9, 0,
- 10, 11, 12, 0, 0, 13, 16, 0, 114, 14,
- 0, 15, 0, 0, 0, 73, 0, 19, 39, 6,
- 16, 0, 17, 9, 0, 10, 11, 12, 0, 18,
- 13, 19, 39, 6, 14, 0, 15, 9, 0, 10,
- 11, 0, 0, 0, 40, 16, 0, 17, 39, 6,
- 15, 134, 135, 0, 73, 10, 19, 0, 10, 16,
- 40, 17, 0, 40, 0, 0, 15, 0, 41, 15,
- 62, 63, 0, 64, 65, 66, 0, 17, 62, 137,
- 17, 64, 65, 66, 43, 0, 0, 43
-};
-
-static const yytype_int16 yycheck[] =
-{
- 1, 17, 18, 23, 1, 18, 16, 99, 9, 90,
- 20, 78, 25, 17, 31, 32, 32, 6, 7, 19,
- 10, 18, 58, 43, 5, 41, 43, 43, 25, 65,
- 66, 25, 26, 19, 125, 32, 10, 129, 41, 43,
- 132, 6, 7, 8, 9, 136, 35, 138, 115, 27,
- 131, 67, 68, 69, 70, 71, 72, 73, 125, 140,
- 31, 32, 78, 67, 68, 69, 70, 71, 72, 136,
- 35, 138, 36, 37, 78, 39, 73, 78, 78, 6,
- 45, 8, 9, 48, 6, 7, 8, 9, 1, 10,
- 90, 5, 78, 23, 6, 7, 39, 43, 114, 115,
- 45, 32, 12, 16, 90, 18, 12, 12, 12, 125,
- 114, 115, 25, 35, 115, 115, 45, 12, 12, 32,
- 136, 125, 138, 36, 125, 125, 46, 115, 41, 115,
- 130, 131, 136, 94, 138, 136, 136, 138, 138, 125,
- 140, -1, -1, 62, 130, 131, -1, 60, 61, -1,
- 136, -1, 138, -1, 140, 0, 1, -1, -1, 4,
- 73, 6, 7, 8, 9, 10, 11, -1, 13, 14,
- 15, -1, -1, 18, -1, -1, -1, 22, -1, 24,
- 6, 7, 8, 9, 6, 7, 8, 9, 33, 11,
- 35, 13, 14, 15, -1, -1, 18, 42, -1, 44,
- 22, -1, 24, 6, 7, 8, 9, -1, 28, 35,
- -1, 33, -1, 35, 34, 35, 36, 37, -1, 39,
- 42, 47, 44, 45, -1, -1, 48, 6, 7, 8,
- 9, -1, 11, -1, 13, 14, 15, -1, -1, 18,
- -1, -1, -1, 22, 47, 24, -1, -1, -1, -1,
- 6, 7, 8, 9, 33, -1, 35, 34, 35, 36,
- 37, -1, 39, 42, -1, 44, 43, -1, 47, 6,
- 7, 8, 9, -1, 11, -1, 13, 14, 15, 35,
- -1, 18, -1, -1, 28, 22, -1, 24, -1, 45,
- 34, 35, 36, 37, -1, 39, 33, -1, 35, 43,
- -1, 34, 35, 36, 37, 42, 39, 44, 45, 6,
- 7, 8, 9, -1, 11, -1, 13, 14, 15, -1,
- -1, 18, -1, 6, 7, 22, -1, 24, 11, -1,
- 13, 14, 15, -1, -1, 18, 33, -1, 35, 22,
- -1, 24, -1, -1, -1, 42, -1, 44, 6, 7,
- 33, -1, 35, 11, -1, 13, 14, 15, -1, 42,
- 18, 44, 6, 7, 22, -1, 24, 11, -1, 13,
- 14, -1, -1, -1, 18, 33, -1, 35, 6, 7,
- 24, 6, 7, -1, 42, 13, 44, -1, 13, 33,
- 18, 35, -1, 18, -1, -1, 24, -1, 42, 24,
- 16, 17, -1, 19, 20, 21, -1, 35, 16, 17,
- 35, 19, 20, 21, 42, -1, -1, 42
-};
-
-/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
- symbol of state STATE-NUM. */
-static const yytype_uint8 yystos[] =
-{
- 0, 50, 0, 1, 4, 6, 7, 8, 9, 11,
- 13, 14, 15, 18, 22, 24, 33, 35, 42, 44,
- 51, 52, 53, 54, 55, 59, 60, 61, 62, 64,
- 10, 5, 41, 6, 9, 59, 27, 65, 66, 6,
- 18, 42, 60, 42, 61, 62, 55, 60, 62, 64,
- 6, 7, 35, 56, 57, 58, 10, 5, 23, 55,
- 31, 32, 16, 17, 19, 20, 21, 28, 34, 35,
- 36, 37, 39, 42, 60, 62, 64, 60, 25, 26,
- 67, 60, 62, 16, 43, 43, 43, 43, 6, 7,
- 45, 65, 60, 60, 47, 56, 57, 59, 70, 71,
- 73, 74, 70, 65, 65, 62, 62, 62, 62, 62,
- 62, 64, 6, 7, 35, 47, 56, 57, 59, 60,
- 61, 62, 63, 64, 68, 69, 72, 74, 58, 71,
- 12, 12, 45, 73, 6, 7, 69, 17, 45, 72,
- 45, 48, 56, 57, 58, 73, 48, 72, 58, 46
-};
-
-#define yyerrok (yyerrstatus = 0)
-#define yyclearin (yychar = YYEMPTY)
-#define YYEMPTY (-2)
-#define YYEOF 0
-
-#define YYACCEPT goto yyacceptlab
-#define YYABORT goto yyabortlab
-#define YYERROR goto yyerrorlab
-
-
-/* Like YYERROR except do call yyerror. This remains here temporarily
- to ease the transition to the new meaning of YYERROR, for GCC.
- Once GCC version 2 has supplanted version 1, this can go. */
-
-#define YYFAIL goto yyerrlab
-
-#define YYRECOVERING() (!!yyerrstatus)
-
-#define YYBACKUP(Token, Value) \
-do \
- if (yychar == YYEMPTY && yylen == 1) \
- { \
- yychar = (Token); \
- yylval = (Value); \
- yytoken = YYTRANSLATE (yychar); \
- YYPOPSTACK (1); \
- goto yybackup; \
- } \
- else \
- { \
- yyerror (scanner, YY_("syntax error: cannot back up")); \
- YYERROR; \
- } \
-while (YYID (0))
-
-
-#define YYTERROR 1
-#define YYERRCODE 256
-
-
-/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
- If N is 0, then set CURRENT to the empty location which ends
- the previous symbol: RHS[0] (always defined). */
-
-#define YYRHSLOC(Rhs, K) ((Rhs)[K])
-#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N) \
- do \
- if (YYID (N)) \
- { \
- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
- } \
- else \
- { \
- (Current).first_line = (Current).last_line = \
- YYRHSLOC (Rhs, 0).last_line; \
- (Current).first_column = (Current).last_column = \
- YYRHSLOC (Rhs, 0).last_column; \
- } \
- while (YYID (0))
-#endif
-
-
-/* YY_LOCATION_PRINT -- Print the location on the stream.
- This macro was not mandated originally: define only if we know
- we won't break user code: when these are the locations we know. */
-
-#ifndef YY_LOCATION_PRINT
-# if YYLTYPE_IS_TRIVIAL
-# define YY_LOCATION_PRINT(File, Loc) \
- fprintf (File, "%d.%d-%d.%d", \
- (Loc).first_line, (Loc).first_column, \
- (Loc).last_line, (Loc).last_column)
-# else
-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
-# endif
-#endif
-
-
-/* YYLEX -- calling `yylex' with the right arguments. */
-
-#ifdef YYLEX_PARAM
-# define YYLEX yylex (&yylval, YYLEX_PARAM)
-#else
-# define YYLEX yylex (&yylval, scanner)
-#endif
-
-/* Enable debugging if requested. */
-#if YYDEBUG
-
-# ifndef YYFPRINTF
-# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
-# define YYFPRINTF fprintf
-# endif
-
-# define YYDPRINTF(Args) \
-do { \
- if (yydebug) \
- YYFPRINTF Args; \
-} while (YYID (0))
-
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
-do { \
- if (yydebug) \
- { \
- YYFPRINTF (stderr, "%s ", Title); \
- yy_symbol_print (stderr, \
- Type, Value, scanner); \
- YYFPRINTF (stderr, "\n"); \
- } \
-} while (YYID (0))
-
-
-/*--------------------------------.
-| Print this symbol on YYOUTPUT. |
-`--------------------------------*/
-
-/*ARGSUSED*/
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t scanner)
-#else
-static void
-yy_symbol_value_print (yyoutput, yytype, yyvaluep, scanner)
- FILE *yyoutput;
- int yytype;
- YYSTYPE const * const yyvaluep;
- yyscan_t scanner;
-#endif
-{
- if (!yyvaluep)
- return;
- YYUSE (scanner);
-# ifdef YYPRINT
- if (yytype < YYNTOKENS)
- YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
-# else
- YYUSE (yyoutput);
-# endif
- switch (yytype)
- {
- default:
- break;
- }
-}
-
-
-/*--------------------------------.
-| Print this symbol on YYOUTPUT. |
-`--------------------------------*/
-
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t scanner)
-#else
-static void
-yy_symbol_print (yyoutput, yytype, yyvaluep, scanner)
- FILE *yyoutput;
- int yytype;
- YYSTYPE const * const yyvaluep;
- yyscan_t scanner;
-#endif
-{
- if (yytype < YYNTOKENS)
- YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
- else
- YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
-
- yy_symbol_value_print (yyoutput, yytype, yyvaluep, scanner);
- YYFPRINTF (yyoutput, ")");
-}
-
-/*------------------------------------------------------------------.
-| yy_stack_print -- Print the state stack from its BOTTOM up to its |
-| TOP (included). |
-`------------------------------------------------------------------*/
-
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
-#else
-static void
-yy_stack_print (bottom, top)
- yytype_int16 *bottom;
- yytype_int16 *top;
-#endif
-{
- YYFPRINTF (stderr, "Stack now");
- for (; bottom <= top; ++bottom)
- YYFPRINTF (stderr, " %d", *bottom);
- YYFPRINTF (stderr, "\n");
-}
-
-# define YY_STACK_PRINT(Bottom, Top) \
-do { \
- if (yydebug) \
- yy_stack_print ((Bottom), (Top)); \
-} while (YYID (0))
-
-
-/*------------------------------------------------.
-| Report that the YYRULE is going to be reduced. |
-`------------------------------------------------*/
-
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yy_reduce_print (YYSTYPE *yyvsp, int yyrule, yyscan_t scanner)
-#else
-static void
-yy_reduce_print (yyvsp, yyrule, scanner)
- YYSTYPE *yyvsp;
- int yyrule;
- yyscan_t scanner;
-#endif
-{
- int yynrhs = yyr2[yyrule];
- int yyi;
- unsigned long int yylno = yyrline[yyrule];
- YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
- yyrule - 1, yylno);
- /* The symbols being reduced. */
- for (yyi = 0; yyi < yynrhs; yyi++)
- {
- fprintf (stderr, " $%d = ", yyi + 1);
- yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
- &(yyvsp[(yyi + 1) - (yynrhs)])
- , scanner);
- fprintf (stderr, "\n");
- }
-}
-
-# define YY_REDUCE_PRINT(Rule) \
-do { \
- if (yydebug) \
- yy_reduce_print (yyvsp, Rule, scanner); \
-} while (YYID (0))
-
-/* Nonzero means print parse trace. It is left uninitialized so that
- multiple parsers can coexist. */
-int yydebug;
-#else /* !YYDEBUG */
-# define YYDPRINTF(Args)
-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
-# define YY_STACK_PRINT(Bottom, Top)
-# define YY_REDUCE_PRINT(Rule)
-#endif /* !YYDEBUG */
-
-
-/* YYINITDEPTH -- initial size of the parser's stacks. */
-#ifndef YYINITDEPTH
-# define YYINITDEPTH 200
-#endif
-
-/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
- if the built-in stack extension method is used).
-
- Do not make this value too large; the results are undefined if
- YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
- evaluated with infinite-precision integer arithmetic. */
-
-#ifndef YYMAXDEPTH
-# define YYMAXDEPTH 10000
-#endif
-
-\f
-
-#if YYERROR_VERBOSE
-
-# ifndef yystrlen
-# if defined __GLIBC__ && defined _STRING_H
-# define yystrlen strlen
-# else
-/* Return the length of YYSTR. */
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static YYSIZE_T
-yystrlen (const char *yystr)
-#else
-static YYSIZE_T
-yystrlen (yystr)
- const char *yystr;
-#endif
-{
- YYSIZE_T yylen;
- for (yylen = 0; yystr[yylen]; yylen++)
- continue;
- return yylen;
-}
-# endif
-# endif
-
-# ifndef yystpcpy
-# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
-# define yystpcpy stpcpy
-# else
-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
- YYDEST. */
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static char *
-yystpcpy (char *yydest, const char *yysrc)
-#else
-static char *
-yystpcpy (yydest, yysrc)
- char *yydest;
- const char *yysrc;
-#endif
-{
- char *yyd = yydest;
- const char *yys = yysrc;
-
- while ((*yyd++ = *yys++) != '\0')
- continue;
-
- return yyd - 1;
-}
-# endif
-# endif
-
-# ifndef yytnamerr
-/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
- quotes and backslashes, so that it's suitable for yyerror. The
- heuristic is that double-quoting is unnecessary unless the string
- contains an apostrophe, a comma, or backslash (other than
- backslash-backslash). YYSTR is taken from yytname. If YYRES is
- null, do not copy; instead, return the length of what the result
- would have been. */
-static YYSIZE_T
-yytnamerr (char *yyres, const char *yystr)
-{
- if (*yystr == '"')
- {
- YYSIZE_T yyn = 0;
- char const *yyp = yystr;
-
- for (;;)
- switch (*++yyp)
- {
- case '\'':
- case ',':
- goto do_not_strip_quotes;
-
- case '\\':
- if (*++yyp != '\\')
- goto do_not_strip_quotes;
- /* Fall through. */
- default:
- if (yyres)
- yyres[yyn] = *yyp;
- yyn++;
- break;
-
- case '"':
- if (yyres)
- yyres[yyn] = '\0';
- return yyn;
- }
- do_not_strip_quotes: ;
- }
-
- if (! yyres)
- return yystrlen (yystr);
-
- return yystpcpy (yyres, yystr) - yyres;
-}
-# endif
-
-/* Copy into YYRESULT an error message about the unexpected token
- YYCHAR while in state YYSTATE. Return the number of bytes copied,
- including the terminating null byte. If YYRESULT is null, do not
- copy anything; just return the number of bytes that would be
- copied. As a special case, return 0 if an ordinary "syntax error"
- message will do. Return YYSIZE_MAXIMUM if overflow occurs during
- size calculation. */
-static YYSIZE_T
-yysyntax_error (char *yyresult, int yystate, int yychar)
-{
- int yyn = yypact[yystate];
-
- if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
- return 0;
- else
- {
- int yytype = YYTRANSLATE (yychar);
- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
- YYSIZE_T yysize = yysize0;
- YYSIZE_T yysize1;
- int yysize_overflow = 0;
- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
- int yyx;
-
-# if 0
- /* This is so xgettext sees the translatable formats that are
- constructed on the fly. */
- YY_("syntax error, unexpected %s");
- YY_("syntax error, unexpected %s, expecting %s");
- YY_("syntax error, unexpected %s, expecting %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s");
- YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
-# endif
- char *yyfmt;
- char const *yyf;
- static char const yyunexpected[] = "syntax error, unexpected %s";
- static char const yyexpecting[] = ", expecting %s";
- static char const yyor[] = " or %s";
- char yyformat[sizeof yyunexpected
- + sizeof yyexpecting - 1
- + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
- * (sizeof yyor - 1))];
- char const *yyprefix = yyexpecting;
-
- /* Start YYX at -YYN if negative to avoid negative indexes in
- YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn + 1;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 1;
-
- yyarg[0] = yytname[yytype];
- yyfmt = yystpcpy (yyformat, yyunexpected);
-
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
- {
- yycount = 1;
- yysize = yysize0;
- yyformat[sizeof yyunexpected - 1] = '\0';
- break;
- }
- yyarg[yycount++] = yytname[yyx];
- yysize1 = yysize + yytnamerr (0, yytname[yyx]);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
- yyfmt = yystpcpy (yyfmt, yyprefix);
- yyprefix = yyor;
- }
-
- yyf = YY_(yyformat);
- yysize1 = yysize + yystrlen (yyf);
- yysize_overflow |= (yysize1 < yysize);
- yysize = yysize1;
-
- if (yysize_overflow)
- return YYSIZE_MAXIMUM;
-
- if (yyresult)
- {
- /* Avoid sprintf, as that infringes on the user's name space.
- Don't have undefined behavior even if the translation
- produced a string with the wrong number of "%s"s. */
- char *yyp = yyresult;
- int yyi = 0;
- while ((*yyp = *yyf) != '\0')
- {
- if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
- {
- yyp += yytnamerr (yyp, yyarg[yyi++]);
- yyf += 2;
- }
- else
- {
- yyp++;
- yyf++;
- }
- }
- }
- return yysize;
- }
-}
-#endif /* YYERROR_VERBOSE */
-\f
-
-/*-----------------------------------------------.
-| Release the memory associated to this symbol. |
-`-----------------------------------------------*/
-
-/*ARGSUSED*/
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-static void
-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, yyscan_t scanner)
-#else
-static void
-yydestruct (yymsg, yytype, yyvaluep, scanner)
- const char *yymsg;
- int yytype;
- YYSTYPE *yyvaluep;
- yyscan_t scanner;
-#endif
-{
- YYUSE (yyvaluep);
- YYUSE (scanner);
-
- if (!yymsg)
- yymsg = "Deleting";
- YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
-
- switch (yytype)
- {
- case 5: /* "HELP_TOPIC" */
-#line 161 "parser.y"
- { free((yyvaluep->str)); };
-#line 1317 "parser.c"
- break;
- case 8: /* "STR" */
-#line 161 "parser.y"
- { free((yyvaluep->str)); };
-#line 1322 "parser.c"
- break;
- case 9: /* "IDENTIFIER" */
-#line 161 "parser.y"
- { free((yyvaluep->str)); };
-#line 1327 "parser.c"
- break;
- case 25: /* "PARAM" */
-#line 162 "parser.y"
- { if((yyvaluep->str)) free((yyvaluep->str)); };
-#line 1332 "parser.c"
- break;
- case 28: /* "CMP_OP" */
-#line 161 "parser.y"
- { free((yyvaluep->str)); };
-#line 1337 "parser.c"
- break;
- case 51: /* "command" */
-#line 163 "parser.y"
- { if((yyvaluep->sel)) _gmx_selelem_free((yyvaluep->sel)); };
-#line 1342 "parser.c"
- break;
- case 52: /* "cmd_plain" */
-#line 163 "parser.y"
- { if((yyvaluep->sel)) _gmx_selelem_free((yyvaluep->sel)); };
-#line 1347 "parser.c"
- break;
- case 55: /* "selection" */
-#line 164 "parser.y"
- { _gmx_selelem_free_chain((yyvaluep->sel)); };
-#line 1352 "parser.c"
- break;
- case 59: /* "string" */
-#line 161 "parser.y"
- { free((yyvaluep->str)); };
-#line 1357 "parser.c"
- break;
- case 60: /* "sel_expr" */
-#line 165 "parser.y"
- { _gmx_selelem_free((yyvaluep->sel)); };
-#line 1362 "parser.c"
- break;
- case 62: /* "num_expr" */
-#line 165 "parser.y"
- { _gmx_selelem_free((yyvaluep->sel)); };
-#line 1367 "parser.c"
- break;
- case 63: /* "str_expr" */
-#line 165 "parser.y"
- { _gmx_selelem_free((yyvaluep->sel)); };
-#line 1372 "parser.c"
- break;
- case 64: /* "pos_expr" */
-#line 165 "parser.y"
- { _gmx_selelem_free((yyvaluep->sel)); };
-#line 1377 "parser.c"
- break;
- case 65: /* "method_params" */
-#line 166 "parser.y"
- { _gmx_selexpr_free_params((yyvaluep->param)); };
-#line 1382 "parser.c"
- break;
- case 66: /* "method_param_list" */
-#line 166 "parser.y"
- { _gmx_selexpr_free_params((yyvaluep->param)); };
-#line 1387 "parser.c"
- break;
- case 67: /* "method_param" */
-#line 166 "parser.y"
- { _gmx_selexpr_free_params((yyvaluep->param)); };
-#line 1392 "parser.c"
- break;
- case 68: /* "value_list" */
-#line 167 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1397 "parser.c"
- break;
- case 69: /* "value_list_contents" */
-#line 167 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1402 "parser.c"
- break;
- case 70: /* "basic_value_list" */
-#line 168 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1407 "parser.c"
- break;
- case 71: /* "basic_value_list_contents" */
-#line 168 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1412 "parser.c"
- break;
- case 72: /* "value_item" */
-#line 167 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1417 "parser.c"
- break;
- case 73: /* "basic_value_item" */
-#line 168 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1422 "parser.c"
- break;
- case 74: /* "value_item_range" */
-#line 167 "parser.y"
- { _gmx_selexpr_free_values((yyvaluep->val)); };
-#line 1427 "parser.c"
- break;
-
- default:
- break;
- }
-}
-\f
-
-/* Prevent warnings from -Wmissing-prototypes. */
-
-#ifdef YYPARSE_PARAM
-#if defined __STDC__ || defined __cplusplus
-int yyparse (void *YYPARSE_PARAM);
-#else
-int yyparse ();
-#endif
-#else /* ! YYPARSE_PARAM */
-#if defined __STDC__ || defined __cplusplus
-int yyparse (yyscan_t scanner);
-#else
-int yyparse ();
-#endif
-#endif /* ! YYPARSE_PARAM */
-
-
-
-
-
-
-/*----------.
-| yyparse. |
-`----------*/
-
-#ifdef YYPARSE_PARAM
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-int
-yyparse (void *YYPARSE_PARAM)
-#else
-int
-yyparse (YYPARSE_PARAM)
- void *YYPARSE_PARAM;
-#endif
-#else /* ! YYPARSE_PARAM */
-#if (defined __STDC__ || defined __C99__FUNC__ \
- || defined __cplusplus || defined _MSC_VER)
-int
-yyparse (yyscan_t scanner)
-#else
-int
-yyparse (scanner)
- yyscan_t scanner;
-#endif
-#endif
-{
- /* The look-ahead symbol. */
-int yychar;
-
-/* The semantic value of the look-ahead symbol. */
-YYSTYPE yylval;
-
-/* Number of syntax errors so far. */
-int yynerrs;
-
- int yystate;
- int yyn;
- int yyresult;
- /* Number of tokens to shift before error messages enabled. */
- int yyerrstatus;
- /* Look-ahead token as an internal (translated) token number. */
- int yytoken = 0;
-#if YYERROR_VERBOSE
- /* Buffer for error messages, and its allocated size. */
- char yymsgbuf[128];
- char *yymsg = yymsgbuf;
- YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
-#endif
-
- /* Three stacks and their tools:
- `yyss': related to states,
- `yyvs': related to semantic values,
- `yyls': related to locations.
-
- Refer to the stacks thru separate pointers, to allow yyoverflow
- to reallocate them elsewhere. */
-
- /* The state stack. */
- yytype_int16 yyssa[YYINITDEPTH];
- yytype_int16 *yyss = yyssa;
- yytype_int16 *yyssp;
-
- /* The semantic value stack. */
- YYSTYPE yyvsa[YYINITDEPTH];
- YYSTYPE *yyvs = yyvsa;
- YYSTYPE *yyvsp;
-
-
-
-#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
-
- YYSIZE_T yystacksize = YYINITDEPTH;
-
- /* The variables used to return semantic value and location from the
- action routines. */
- YYSTYPE yyval;
-
-
- /* The number of symbols on the RHS of the reduced rule.
- Keep to zero when no symbol should be popped. */
- int yylen = 0;
-
- YYDPRINTF ((stderr, "Starting parse\n"));
-
- yystate = 0;
- yyerrstatus = 0;
- yynerrs = 0;
- yychar = YYEMPTY; /* Cause a token to be read. */
-
- /* Initialize stack pointers.
- Waste one element of value and location stack
- so that they stay on the same level as the state stack.
- The wasted elements are never initialized. */
-
- yyssp = yyss;
- yyvsp = yyvs;
-
- goto yysetstate;
-
-/*------------------------------------------------------------.
-| yynewstate -- Push a new state, which is found in yystate. |
-`------------------------------------------------------------*/
- yynewstate:
- /* In all cases, when you get here, the value and location stacks
- have just been pushed. So pushing a state here evens the stacks. */
- yyssp++;
-
- yysetstate:
- *yyssp = yystate;
-
- if (yyss + yystacksize - 1 <= yyssp)
- {
- /* Get the current used size of the three stacks, in elements. */
- YYSIZE_T yysize = yyssp - yyss + 1;
-
-#ifdef yyoverflow
- {
- /* Give user a chance to reallocate the stack. Use copies of
- these so that the &'s don't force the real ones into
- memory. */
- YYSTYPE *yyvs1 = yyvs;
- yytype_int16 *yyss1 = yyss;
-
-
- /* Each stack pointer address is followed by the size of the
- data in use in that stack, in bytes. This used to be a
- conditional around just the two extra args, but that might
- be undefined if yyoverflow is a macro. */
- yyoverflow (YY_("memory exhausted"),
- &yyss1, yysize * sizeof (*yyssp),
- &yyvs1, yysize * sizeof (*yyvsp),
-
- &yystacksize);
-
- yyss = yyss1;
- yyvs = yyvs1;
- }
-#else /* no yyoverflow */
-# ifndef YYSTACK_RELOCATE
- goto yyexhaustedlab;
-# else
- /* Extend the stack our own way. */
- if (YYMAXDEPTH <= yystacksize)
- goto yyexhaustedlab;
- yystacksize *= 2;
- if (YYMAXDEPTH < yystacksize)
- yystacksize = YYMAXDEPTH;
-
- {
- yytype_int16 *yyss1 = yyss;
- union yyalloc *yyptr =
- (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
- if (! yyptr)
- goto yyexhaustedlab;
- YYSTACK_RELOCATE (yyss);
- YYSTACK_RELOCATE (yyvs);
-
-# undef YYSTACK_RELOCATE
- if (yyss1 != yyssa)
- YYSTACK_FREE (yyss1);
- }
-# endif
-#endif /* no yyoverflow */
-
- yyssp = yyss + yysize - 1;
- yyvsp = yyvs + yysize - 1;
-
-
- YYDPRINTF ((stderr, "Stack size increased to %lu\n",
- (unsigned long int) yystacksize));
-
- if (yyss + yystacksize - 1 <= yyssp)
- YYABORT;
- }
-
- YYDPRINTF ((stderr, "Entering state %d\n", yystate));
-
- goto yybackup;
-
-/*-----------.
-| yybackup. |
-`-----------*/
-yybackup:
-
- /* Do appropriate processing given the current state. Read a
- look-ahead token if we need one and don't already have one. */
-
- /* First try to decide what to do without reference to look-ahead token. */
- yyn = yypact[yystate];
- if (yyn == YYPACT_NINF)
- goto yydefault;
-
- /* Not known => get a look-ahead token if don't already have one. */
-
- /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
- if (yychar == YYEMPTY)
- {
- YYDPRINTF ((stderr, "Reading a token: "));
- yychar = YYLEX;
- }
-
- if (yychar <= YYEOF)
- {
- yychar = yytoken = YYEOF;
- YYDPRINTF ((stderr, "Now at end of input.\n"));
- }
- else
- {
- yytoken = YYTRANSLATE (yychar);
- YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
- }
-
- /* If the proper action on seeing token YYTOKEN is to reduce or to
- detect an error, take that action. */
- yyn += yytoken;
- if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
- goto yydefault;
- yyn = yytable[yyn];
- if (yyn <= 0)
- {
- if (yyn == 0 || yyn == YYTABLE_NINF)
- goto yyerrlab;
- yyn = -yyn;
- goto yyreduce;
- }
-
- if (yyn == YYFINAL)
- YYACCEPT;
-
- /* Count tokens shifted since error; after three, turn off error
- status. */
- if (yyerrstatus)
- yyerrstatus--;
-
- /* Shift the look-ahead token. */
- YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
-
- /* Discard the shifted token unless it is eof. */
- if (yychar != YYEOF)
- yychar = YYEMPTY;
-
- yystate = yyn;
- *++yyvsp = yylval;
-
- goto yynewstate;
-
-
-/*-----------------------------------------------------------.
-| yydefault -- do the default action for the current state. |
-`-----------------------------------------------------------*/
-yydefault:
- yyn = yydefact[yystate];
- if (yyn == 0)
- goto yyerrlab;
- goto yyreduce;
-
-
-/*-----------------------------.
-| yyreduce -- Do a reduction. |
-`-----------------------------*/
-yyreduce:
- /* yyn is the number of a rule to reduce with. */
- yylen = yyr2[yyn];
-
- /* If YYLEN is nonzero, implement the default value of the action:
- `$$ = $1'.
-
- Otherwise, the following line sets YYVAL to garbage.
- This behavior is undocumented and Bison
- users should not rely upon it. Assigning to YYVAL
- unconditionally makes the parser a bit smaller, and it avoids a
- GCC warning that YYVAL may be used uninitialized. */
- yyval = yyvsp[1-yylen];
-
-
- YY_REDUCE_PRINT (yyn);
- switch (yyn)
- {
- case 2:
-#line 182 "parser.y"
- { (yyval.sel) = NULL ;}
- break;
-
- case 3:
-#line 184 "parser.y"
- {
- (yyval.sel) = _gmx_sel_append_selection((yyvsp[(2) - (2)].sel), (yyvsp[(1) - (2)].sel), scanner);
- if (_gmx_sel_parser_should_finish(scanner))
- YYACCEPT;
- ;}
- break;
-
- case 4:
-#line 192 "parser.y"
- { (yyval.sel) = (yyvsp[(1) - (2)].sel); ;}
- break;
-
- case 5:
-#line 194 "parser.y"
- {
- (yyval.sel) = NULL;
- _gmx_selparser_error("invalid selection '%s'",
- _gmx_sel_lexer_pselstr(scanner));
- _gmx_sel_lexer_clear_method_stack(scanner);
- if (_gmx_sel_is_lexer_interactive(scanner))
- {
- _gmx_sel_lexer_clear_pselstr(scanner);
- yyerrok;
- }
- else
- {
- YYABORT;
- }
- ;}
- break;
-
- case 6:
-#line 213 "parser.y"
- {
- (yyval.sel) = NULL;
- _gmx_sel_handle_empty_cmd(scanner);
- ;}
- break;
-
- case 7:
-#line 217 "parser.y"
- { (yyval.sel) = NULL; ;}
- break;
-
- case 8:
-#line 219 "parser.y"
- {
- t_selelem *s, *p;
- s = _gmx_sel_init_group_by_id((yyvsp[(1) - (1)].i), scanner);
- if (s == NULL) YYERROR;
- p = _gmx_sel_init_position(s, NULL, scanner);
- if (p == NULL) YYERROR;
- (yyval.sel) = _gmx_sel_init_selection(strdup(s->name), p, scanner);
- ;}
- break;
-
- case 9:
-#line 228 "parser.y"
- {
- t_selelem *s, *p;
- s = _gmx_sel_init_group_by_name((yyvsp[(1) - (1)].str), scanner);
- free((yyvsp[(1) - (1)].str));
- if (s == NULL) YYERROR;
- p = _gmx_sel_init_position(s, NULL, scanner);
- if (p == NULL) YYERROR;
- (yyval.sel) = _gmx_sel_init_selection(strdup(s->name), p, scanner);
- ;}
- break;
-
- case 10:
-#line 238 "parser.y"
- { (yyval.sel) = _gmx_sel_init_selection(NULL, (yyvsp[(1) - (1)].sel), scanner); ;}
- break;
-
- case 11:
-#line 240 "parser.y"
- { (yyval.sel) = _gmx_sel_init_selection((yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].sel), scanner); ;}
- break;
-
- case 12:
-#line 242 "parser.y"
- { (yyval.sel) = _gmx_sel_assign_variable((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].sel), scanner); ;}
- break;
-
- case 13:
-#line 244 "parser.y"
- { (yyval.sel) = _gmx_sel_assign_variable((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].sel), scanner); ;}
- break;
-
- case 14:
-#line 246 "parser.y"
- { (yyval.sel) = _gmx_sel_assign_variable((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].sel), scanner); ;}
- break;
-
- case 15:
-#line 251 "parser.y"
- { _gmx_sel_handle_help_cmd(NULL, scanner); ;}
- break;
-
- case 17:
-#line 255 "parser.y"
- { _gmx_sel_handle_help_cmd((yyvsp[(2) - (2)].str), scanner); ;}
- break;
-
- case 18:
-#line 256 "parser.y"
- { _gmx_sel_handle_help_cmd((yyvsp[(2) - (2)].str), scanner); ;}
- break;
-
- case 19:
-#line 260 "parser.y"
- { (yyval.sel) = (yyvsp[(1) - (1)].sel); ;}
- break;
-
- case 20:
-#line 262 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_position((yyvsp[(1) - (1)].sel), NULL, scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 21:
-#line 266 "parser.y"
- { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
- break;
-
- case 22:
-#line 268 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_modifier((yyvsp[(2) - (3)].meth), (yyvsp[(3) - (3)].param), (yyvsp[(1) - (3)].sel), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 23:
-#line 279 "parser.y"
- { (yyval.r) = (yyvsp[(1) - (1)].i); ;}
- break;
-
- case 24:
-#line 280 "parser.y"
- { (yyval.r) = -(yyvsp[(2) - (2)].i); ;}
- break;
-
- case 25:
-#line 284 "parser.y"
- { (yyval.r) = (yyvsp[(1) - (1)].r); ;}
- break;
-
- case 26:
-#line 285 "parser.y"
- { (yyval.r) = -(yyvsp[(2) - (2)].r); ;}
- break;
-
- case 27:
-#line 288 "parser.y"
- { (yyval.r) = (yyvsp[(1) - (1)].r); ;}
- break;
-
- case 28:
-#line 289 "parser.y"
- { (yyval.r) = (yyvsp[(1) - (1)].r); ;}
- break;
-
- case 29:
-#line 292 "parser.y"
- { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
- break;
-
- case 30:
-#line 293 "parser.y"
- { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
- break;
-
- case 31:
-#line 302 "parser.y"
- {
- (yyval.sel) = _gmx_selelem_create(SEL_BOOLEAN);
- (yyval.sel)->u.boolt = BOOL_NOT;
- (yyval.sel)->child = (yyvsp[(2) - (2)].sel);
- ;}
- break;
-
- case 32:
-#line 308 "parser.y"
- {
- (yyval.sel) = _gmx_selelem_create(SEL_BOOLEAN);
- (yyval.sel)->u.boolt = BOOL_AND;
- (yyval.sel)->child = (yyvsp[(1) - (3)].sel); (yyval.sel)->child->next = (yyvsp[(3) - (3)].sel);
- ;}
- break;
-
- case 33:
-#line 314 "parser.y"
- {
- (yyval.sel) = _gmx_selelem_create(SEL_BOOLEAN);
- (yyval.sel)->u.boolt = BOOL_OR;
- (yyval.sel)->child = (yyvsp[(1) - (3)].sel); (yyval.sel)->child->next = (yyvsp[(3) - (3)].sel);
- ;}
- break;
-
- case 34:
-#line 325 "parser.y"
- { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
- break;
-
- case 35:
-#line 330 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_comparison((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), (yyvsp[(2) - (3)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 36:
-#line 338 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_group_by_name((yyvsp[(2) - (2)].str), scanner);
- free((yyvsp[(2) - (2)].str));
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 37:
-#line 344 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_group_by_id((yyvsp[(2) - (2)].i), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 38:
-#line 351 "parser.y"
- { (yyval.str) = NULL; ;}
- break;
-
- case 39:
-#line 352 "parser.y"
- { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
- break;
-
- case 40:
-#line 357 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (2)].meth), NULL, (yyvsp[(1) - (2)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 41:
-#line 362 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (3)].meth), process_value_list((yyvsp[(3) - (3)].val), NULL), (yyvsp[(1) - (3)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 42:
-#line 367 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (3)].meth), process_value_list((yyvsp[(3) - (3)].val), NULL), (yyvsp[(1) - (3)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 43:
-#line 375 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_method((yyvsp[(2) - (3)].meth), (yyvsp[(3) - (3)].param), (yyvsp[(1) - (3)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 44:
-#line 387 "parser.y"
- {
- (yyval.sel) = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype((yyval.sel), INT_VALUE);
- _gmx_selvalue_reserve(&(yyval.sel)->v, 1);
- (yyval.sel)->v.u.i[0] = (yyvsp[(1) - (1)].i);
- ;}
- break;
-
- case 45:
-#line 394 "parser.y"
- {
- (yyval.sel) = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype((yyval.sel), REAL_VALUE);
- _gmx_selvalue_reserve(&(yyval.sel)->v, 1);
- (yyval.sel)->v.u.r[0] = (yyvsp[(1) - (1)].r);
- ;}
- break;
-
- case 46:
-#line 404 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (2)].meth), NULL, (yyvsp[(1) - (2)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 47:
-#line 409 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_method((yyvsp[(2) - (3)].meth), (yyvsp[(3) - (3)].param), (yyvsp[(1) - (3)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 48:
-#line 417 "parser.y"
- { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '+', scanner); ;}
- break;
-
- case 49:
-#line 419 "parser.y"
- { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '-', scanner); ;}
- break;
-
- case 50:
-#line 421 "parser.y"
- { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '*', scanner); ;}
- break;
-
- case 51:
-#line 423 "parser.y"
- { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '/', scanner); ;}
- break;
-
- case 52:
-#line 425 "parser.y"
- { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(2) - (2)].sel), NULL, '-', scanner); ;}
- break;
-
- case 53:
-#line 427 "parser.y"
- { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '^', scanner); ;}
- break;
-
- case 54:
-#line 428 "parser.y"
- { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
- break;
-
- case 55:
-#line 436 "parser.y"
- {
- (yyval.sel) = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype((yyval.sel), STR_VALUE);
- _gmx_selvalue_reserve(&(yyval.sel)->v, 1);
- (yyval.sel)->v.u.s[0] = (yyvsp[(1) - (1)].str);
- ;}
- break;
-
- case 56:
-#line 443 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (2)].meth), NULL, (yyvsp[(1) - (2)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 57:
-#line 455 "parser.y"
- { (yyval.sel) = _gmx_sel_init_const_position((yyvsp[(2) - (7)].r), (yyvsp[(4) - (7)].r), (yyvsp[(6) - (7)].r)); ;}
- break;
-
- case 58:
-#line 459 "parser.y"
- { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
- break;
-
- case 59:
-#line 464 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_method((yyvsp[(1) - (2)].meth), (yyvsp[(2) - (2)].param), NULL, scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 60:
-#line 472 "parser.y"
- {
- (yyval.sel) = _gmx_sel_init_position((yyvsp[(3) - (3)].sel), (yyvsp[(1) - (3)].str), scanner);
- if ((yyval.sel) == NULL) YYERROR;
- ;}
- break;
-
- case 61:
-#line 483 "parser.y"
- { (yyval.sel) = _gmx_sel_init_variable_ref((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 62:
-#line 487 "parser.y"
- { (yyval.sel) = _gmx_sel_init_variable_ref((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 63:
-#line 491 "parser.y"
- { (yyval.sel) = _gmx_sel_init_variable_ref((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 64:
-#line 500 "parser.y"
- { (yyval.param) = process_param_list((yyvsp[(1) - (1)].param)); ;}
- break;
-
- case 65:
-#line 502 "parser.y"
- { (yyval.param) = process_param_list((yyvsp[(1) - (2)].param)); ;}
- break;
-
- case 66:
-#line 506 "parser.y"
- { (yyval.param) = NULL; ;}
- break;
-
- case 67:
-#line 508 "parser.y"
- { (yyvsp[(2) - (2)].param)->next = (yyvsp[(1) - (2)].param); (yyval.param) = (yyvsp[(2) - (2)].param); ;}
- break;
-
- case 68:
-#line 513 "parser.y"
- {
- (yyval.param) = _gmx_selexpr_create_param((yyvsp[(1) - (2)].str));
- (yyval.param)->value = process_value_list((yyvsp[(2) - (2)].val), &(yyval.param)->nval);
- ;}
- break;
-
- case 69:
-#line 519 "parser.y"
- { (yyval.val) = NULL; ;}
- break;
-
- case 70:
-#line 520 "parser.y"
- { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
- break;
-
- case 71:
-#line 521 "parser.y"
- { (yyval.val) = (yyvsp[(2) - (3)].val); ;}
- break;
-
- case 72:
-#line 525 "parser.y"
- { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
- break;
-
- case 73:
-#line 527 "parser.y"
- { (yyvsp[(2) - (2)].val)->next = (yyvsp[(1) - (2)].val); (yyval.val) = (yyvsp[(2) - (2)].val); ;}
- break;
-
- case 74:
-#line 529 "parser.y"
- { (yyvsp[(3) - (3)].val)->next = (yyvsp[(1) - (3)].val); (yyval.val) = (yyvsp[(3) - (3)].val); ;}
- break;
-
- case 75:
-#line 533 "parser.y"
- { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
- break;
-
- case 76:
-#line 534 "parser.y"
- { (yyval.val) = (yyvsp[(2) - (3)].val); ;}
- break;
-
- case 77:
-#line 538 "parser.y"
- { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
- break;
-
- case 78:
-#line 540 "parser.y"
- { (yyvsp[(2) - (2)].val)->next = (yyvsp[(1) - (2)].val); (yyval.val) = (yyvsp[(2) - (2)].val); ;}
- break;
-
- case 79:
-#line 542 "parser.y"
- { (yyvsp[(3) - (3)].val)->next = (yyvsp[(1) - (3)].val); (yyval.val) = (yyvsp[(3) - (3)].val); ;}
- break;
-
- case 80:
-#line 546 "parser.y"
- { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 81:
-#line 548 "parser.y"
- { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 82:
-#line 550 "parser.y"
- { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 83:
-#line 552 "parser.y"
- { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
- break;
-
- case 84:
-#line 553 "parser.y"
- { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
- break;
-
- case 85:
-#line 558 "parser.y"
- {
- (yyval.val) = _gmx_selexpr_create_value(INT_VALUE);
- (yyval.val)->u.i.i1 = (yyval.val)->u.i.i2 = (yyvsp[(1) - (1)].r);
- ;}
- break;
-
- case 86:
-#line 563 "parser.y"
- {
- (yyval.val) = _gmx_selexpr_create_value(REAL_VALUE);
- (yyval.val)->u.r.r1 = (yyval.val)->u.r.r2 = (yyvsp[(1) - (1)].r);
- ;}
- break;
-
- case 87:
-#line 568 "parser.y"
- {
- (yyval.val) = _gmx_selexpr_create_value(STR_VALUE);
- (yyval.val)->u.s = (yyvsp[(1) - (1)].str);
- ;}
- break;
-
- case 88:
-#line 572 "parser.y"
- { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
- break;
-
- case 89:
-#line 577 "parser.y"
- {
- (yyval.val) = _gmx_selexpr_create_value(INT_VALUE);
- (yyval.val)->u.i.i1 = (yyvsp[(1) - (3)].r); (yyval.val)->u.i.i2 = (yyvsp[(3) - (3)].r);
- ;}
- break;
-
- case 90:
-#line 582 "parser.y"
- {
- (yyval.val) = _gmx_selexpr_create_value(REAL_VALUE);
- (yyval.val)->u.r.r1 = (yyvsp[(1) - (3)].r); (yyval.val)->u.r.r2 = (yyvsp[(3) - (3)].r);
- ;}
- break;
-
- case 91:
-#line 587 "parser.y"
- {
- (yyval.val) = _gmx_selexpr_create_value(REAL_VALUE);
- (yyval.val)->u.r.r1 = (yyvsp[(1) - (3)].r); (yyval.val)->u.r.r2 = (yyvsp[(3) - (3)].r);
- ;}
- break;
-
-
-/* Line 1267 of yacc.c. */
-#line 2309 "parser.c"
- default: break;
- }
- YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
-
- YYPOPSTACK (yylen);
- yylen = 0;
- YY_STACK_PRINT (yyss, yyssp);
-
- *++yyvsp = yyval;
-
-
- /* Now `shift' the result of the reduction. Determine what state
- that goes to, based on the state we popped back to and the rule
- number reduced by. */
-
- yyn = yyr1[yyn];
-
- yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
- if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
- yystate = yytable[yystate];
- else
- yystate = yydefgoto[yyn - YYNTOKENS];
-
- goto yynewstate;
-
-
-/*------------------------------------.
-| yyerrlab -- here on detecting error |
-`------------------------------------*/
-yyerrlab:
- /* If not already recovering from an error, report this error. */
- if (!yyerrstatus)
- {
- ++yynerrs;
-#if ! YYERROR_VERBOSE
- yyerror (scanner, YY_("syntax error"));
-#else
- {
- YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
- if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
- {
- YYSIZE_T yyalloc = 2 * yysize;
- if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
- yyalloc = YYSTACK_ALLOC_MAXIMUM;
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
- yymsg = (char *) YYSTACK_ALLOC (yyalloc);
- if (yymsg)
- yymsg_alloc = yyalloc;
- else
- {
- yymsg = yymsgbuf;
- yymsg_alloc = sizeof yymsgbuf;
- }
- }
-
- if (0 < yysize && yysize <= yymsg_alloc)
- {
- (void) yysyntax_error (yymsg, yystate, yychar);
- yyerror (scanner, yymsg);
- }
- else
- {
- yyerror (scanner, YY_("syntax error"));
- if (yysize != 0)
- goto yyexhaustedlab;
- }
- }
-#endif
- }
-
-
-
- if (yyerrstatus == 3)
- {
- /* If just tried and failed to reuse look-ahead token after an
- error, discard it. */
-
- if (yychar <= YYEOF)
- {
- /* Return failure if at end of input. */
- if (yychar == YYEOF)
- YYABORT;
- }
- else
- {
- yydestruct ("Error: discarding",
- yytoken, &yylval, scanner);
- yychar = YYEMPTY;
- }
- }
-
- /* Else will try to reuse look-ahead token after shifting the error
- token. */
- goto yyerrlab1;
-
-
-/*---------------------------------------------------.
-| yyerrorlab -- error raised explicitly by YYERROR. |
-`---------------------------------------------------*/
-yyerrorlab:
-
- /* Pacify compilers like GCC when the user code never invokes
- YYERROR and the label yyerrorlab therefore never appears in user
- code. */
- if (/*CONSTCOND*/ 0)
- goto yyerrorlab;
-
- /* Do not reclaim the symbols of the rule which action triggered
- this YYERROR. */
- YYPOPSTACK (yylen);
- yylen = 0;
- YY_STACK_PRINT (yyss, yyssp);
- yystate = *yyssp;
- goto yyerrlab1;
-
-
-/*-------------------------------------------------------------.
-| yyerrlab1 -- common code for both syntax error and YYERROR. |
-`-------------------------------------------------------------*/
-yyerrlab1:
- yyerrstatus = 3; /* Each real token shifted decrements this. */
-
- for (;;)
- {
- yyn = yypact[yystate];
- if (yyn != YYPACT_NINF)
- {
- yyn += YYTERROR;
- if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
- {
- yyn = yytable[yyn];
- if (0 < yyn)
- break;
- }
- }
-
- /* Pop the current state because it cannot handle the error token. */
- if (yyssp == yyss)
- YYABORT;
-
-
- yydestruct ("Error: popping",
- yystos[yystate], yyvsp, scanner);
- YYPOPSTACK (1);
- yystate = *yyssp;
- YY_STACK_PRINT (yyss, yyssp);
- }
-
- if (yyn == YYFINAL)
- YYACCEPT;
-
- *++yyvsp = yylval;
-
-
- /* Shift the error token. */
- YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
-
- yystate = yyn;
- goto yynewstate;
-
-
-/*-------------------------------------.
-| yyacceptlab -- YYACCEPT comes here. |
-`-------------------------------------*/
-yyacceptlab:
- yyresult = 0;
- goto yyreturn;
-
-/*-----------------------------------.
-| yyabortlab -- YYABORT comes here. |
-`-----------------------------------*/
-yyabortlab:
- yyresult = 1;
- goto yyreturn;
-
-#ifndef yyoverflow
-/*-------------------------------------------------.
-| yyexhaustedlab -- memory exhaustion comes here. |
-`-------------------------------------------------*/
-yyexhaustedlab:
- yyerror (scanner, YY_("memory exhausted"));
- yyresult = 2;
- /* Fall through. */
-#endif
-
-yyreturn:
- if (yychar != YYEOF && yychar != YYEMPTY)
- yydestruct ("Cleanup: discarding lookahead",
- yytoken, &yylval, scanner);
- /* Do not reclaim the symbols of the rule which action triggered
- this YYABORT or YYACCEPT. */
- YYPOPSTACK (yylen);
- YY_STACK_PRINT (yyss, yyssp);
- while (yyssp != yyss)
- {
- yydestruct ("Cleanup: popping",
- yystos[*yyssp], yyvsp, scanner);
- YYPOPSTACK (1);
- }
-#ifndef yyoverflow
- if (yyss != yyssa)
- YYSTACK_FREE (yyss);
-#endif
-#if YYERROR_VERBOSE
- if (yymsg != yymsgbuf)
- YYSTACK_FREE (yymsg);
-#endif
- /* Make sure YYID is used. */
- return YYID (yyresult);
-}
-
-
-#line 593 "parser.y"
-
-
-static t_selexpr_value *
-process_value_list(t_selexpr_value *values, int *nr)
-{
- t_selexpr_value *val, *pval, *nval;
-
- /* Count values (if needed) and reverse list */
- if (nr)
- {
- *nr = 0;
- }
- pval = NULL;
- val = values;
- while (val)
- {
- if (nr)
- {
- ++*nr;
- }
- nval = val->next;
- val->next = pval;
- pval = val;
- val = nval;
- }
- values = pval;
-
- return values;
-}
-
-static t_selexpr_param *
-process_param_list(t_selexpr_param *params)
-{
- t_selexpr_param *par, *ppar, *npar;
-
- /* Reverse list */
- ppar = NULL;
- par = params;
- while (par)
- {
- npar = par->next;
- par->next = ppar;
- ppar = par;
- par = npar;
- }
- params = ppar;
-
- return params;
-}
-
-static void
-yyerror(yyscan_t scanner, char const *s)
-{
- _gmx_selparser_error("%s", s);
-}
-
-
-
+++ /dev/null
-/* A Bison parser, made by GNU Bison 2.3. */
-
-/* Skeleton interface for Bison's Yacc-like parsers in C
-
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
-
-/* As a special exception, you may create a larger work that contains
- part or all of the Bison parser skeleton and distribute that work
- under terms of your choice, so long as that work isn't itself a
- parser generator using the skeleton or a modified version thereof
- as a parser skeleton. Alternatively, if you modify or redistribute
- the parser skeleton itself, you may (at your option) remove this
- special exception, which will cause the skeleton and the resulting
- Bison output files to be licensed under the GNU General Public
- License without this special exception.
-
- This special exception was added by the Free Software Foundation in
- version 2.2 of Bison. */
-
-/* Tokens. */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
- /* Put the tokens into the symbol table, so that GDB and other debuggers
- know about them. */
- enum yytokentype {
- INVALID = 258,
- HELP = 259,
- HELP_TOPIC = 260,
- TOK_INT = 261,
- TOK_REAL = 262,
- STR = 263,
- IDENTIFIER = 264,
- CMD_SEP = 265,
- GROUP = 266,
- TO = 267,
- VARIABLE_NUMERIC = 268,
- VARIABLE_GROUP = 269,
- VARIABLE_POS = 270,
- KEYWORD_NUMERIC = 271,
- KEYWORD_STR = 272,
- KEYWORD_POS = 273,
- KEYWORD_GROUP = 274,
- METHOD_NUMERIC = 275,
- METHOD_GROUP = 276,
- METHOD_POS = 277,
- MODIFIER = 278,
- EMPTY_POSMOD = 279,
- PARAM = 280,
- END_OF_METHOD = 281,
- OF = 282,
- CMP_OP = 283,
- PARAM_REDUCT = 284,
- XOR = 285,
- OR = 286,
- AND = 287,
- NOT = 288,
- UNARY_NEG = 289,
- NUM_REDUCT = 290
- };
-#endif
-/* Tokens. */
-#define INVALID 258
-#define HELP 259
-#define HELP_TOPIC 260
-#define TOK_INT 261
-#define TOK_REAL 262
-#define STR 263
-#define IDENTIFIER 264
-#define CMD_SEP 265
-#define GROUP 266
-#define TO 267
-#define VARIABLE_NUMERIC 268
-#define VARIABLE_GROUP 269
-#define VARIABLE_POS 270
-#define KEYWORD_NUMERIC 271
-#define KEYWORD_STR 272
-#define KEYWORD_POS 273
-#define KEYWORD_GROUP 274
-#define METHOD_NUMERIC 275
-#define METHOD_GROUP 276
-#define METHOD_POS 277
-#define MODIFIER 278
-#define EMPTY_POSMOD 279
-#define PARAM 280
-#define END_OF_METHOD 281
-#define OF 282
-#define CMP_OP 283
-#define PARAM_REDUCT 284
-#define XOR 285
-#define OR 286
-#define AND 287
-#define NOT 288
-#define UNARY_NEG 289
-#define NUM_REDUCT 290
-
-
-
-
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-typedef union YYSTYPE
-#line 62 "parser.y"
-{
- int i;
- real r;
- char *str;
- struct gmx_ana_selmethod_t *meth;
-
- struct t_selelem *sel;
-
- struct t_selexpr_value *val;
- struct t_selexpr_param *param;
-}
-/* Line 1489 of yacc.c. */
-#line 131 "parser.h"
- YYSTYPE;
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
-# define YYSTYPE_IS_TRIVIAL 1
-#endif
-
-
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Grammar description and parser for the selection language.
- */
-%{
-/*! \internal \file parser.c
- * \brief Generated (from parser.y by Bison) parser for the selection language.
- */
-/*! \internal \file parser.h
- * \brief Generated (from parser.y by Bison) parser include file.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-
-#include <string2.h>
-
-#include "parsetree.h"
-#include "selelem.h"
-
-#include "scanner.h"
-
-static t_selexpr_value *
-process_value_list(t_selexpr_value *values, int *nr);
-static t_selexpr_param *
-process_param_list(t_selexpr_param *params);
-
-static void
-yyerror(yyscan_t, char const *s);
-%}
-
-%union{
- int i;
- real r;
- char *str;
- struct gmx_ana_selmethod_t *meth;
-
- struct t_selelem *sel;
-
- struct t_selexpr_value *val;
- struct t_selexpr_param *param;
-};
-/* NOTE: The Intel compiler seems to report warnings for the above line about
- * "definition at end of file not followed by a semicolon or a declarator".
- * This is due to the compiler misinterpreting #line directives in the
- * generated files parser.c/.h, and changing them would be more trouble than
- * it's worth. */
-
-/* Invalid token to report lexer errors */
-%token INVALID
-
-/* Tokens for help requests */
-%token HELP
-%token <str> HELP_TOPIC
-
-/* Simple input tokens */
-%token <i> TOK_INT
-%token <r> TOK_REAL
-%token <str> STR
-%token <str> IDENTIFIER
-%token CMD_SEP
-
-/* Simple keyword tokens */
-%token GROUP
-%token TO
-
-/* Variable tokens */
-%token <sel> VARIABLE_NUMERIC
-%token <sel> VARIABLE_GROUP
-%token <sel> VARIABLE_POS
-
-/* Selection method tokens */
-%token <meth> KEYWORD_NUMERIC
-%token <meth> KEYWORD_STR
-%token <str> KEYWORD_POS
-%token <meth> KEYWORD_GROUP
-%token <meth> METHOD_NUMERIC
-%token <meth> METHOD_GROUP
-%token <meth> METHOD_POS
-%token <meth> MODIFIER
-/* Empty token that should precede any non-position KEYWORD/METHOD token that
- * is not preceded by KEYWORD_POS. This is used to work around reduce/reduce
- * conflicts that appear when a lookahead token would require a reduction of
- * a rule with empty RHS before shifting, and there is an alternative reduction
- * available. Replacing the empty RHS with a dummy token makes these conflicts
- * only shift/reduce conflicts. Another alternative would be to remove the
- * pos_mod non-terminal completely and split each rule that uses it into two,
- * but this would require duplicating six rules in the grammar. */
-%token EMPTY_POSMOD
-
-%token <str> PARAM
-%token END_OF_METHOD
-
-%token OF
-/* Comparison operators have lower precedence than parameter reduction
- * to make it possible to parse, e.g., "mindist from resnr 1 < 2" without
- * parenthesis. */
-%nonassoc <str> CMP_OP
-/* A dummy token that determines the precedence of parameter reduction */
-%nonassoc PARAM_REDUCT
-/* Boolean operator tokens */
-%left OR XOR
-%left AND
-%left NOT
-/* Arithmetic operator tokens */
-%left '+' '-'
-%left '*' '/'
-%right UNARY_NEG /* Dummy token for unary negation precedence */
-%right '^'
-%nonassoc NUM_REDUCT /* Dummy token for numerical keyword reduction precedence */
-
-/* Simple non-terminals */
-%type <r> integer_number
-%type <r> real_number number
-%type <str> string
-%type <str> pos_mod
-
-/* Expression non-terminals */
-%type <sel> commands command cmd_plain
-%type <sel> selection
-%type <sel> sel_expr
-%type <sel> num_expr
-%type <sel> str_expr
-%type <sel> pos_expr
-
-/* Parameter/value non-terminals */
-%type <param> method_params method_param_list method_param
-%type <val> value_list value_list_contents value_item value_item_range
-%type <val> basic_value_list basic_value_list_contents basic_value_item
-
-%destructor { free($$); } HELP_TOPIC STR IDENTIFIER CMP_OP string
-%destructor { if($$) free($$); } PARAM
-%destructor { if($$) _gmx_selelem_free($$); } command cmd_plain
-%destructor { _gmx_selelem_free_chain($$); } selection
-%destructor { _gmx_selelem_free($$); } sel_expr num_expr str_expr pos_expr
-%destructor { _gmx_selexpr_free_params($$); } method_params method_param_list method_param
-%destructor { _gmx_selexpr_free_values($$); } value_list value_list_contents value_item value_item_range
-%destructor { _gmx_selexpr_free_values($$); } basic_value_list basic_value_list_contents basic_value_item
-
-%expect 50
-%debug
-%pure-parser
-
-/* If you change these, you also need to update the prototype in parsetree.c. */
-%name-prefix="_gmx_sel_yyb"
-%parse-param { yyscan_t scanner }
-%lex-param { yyscan_t scanner }
-
-%%
-
-/* The start rule: allow one or more commands */
-commands: /* empty */ { $$ = NULL }
- | commands command
- {
- $$ = _gmx_sel_append_selection($2, $1, scanner);
- if (_gmx_sel_parser_should_finish(scanner))
- YYACCEPT;
- }
-;
-
-/* A command is formed from an actual command and a separator */
-command: cmd_plain CMD_SEP { $$ = $1; }
- | error CMD_SEP
- {
- $$ = NULL;
- _gmx_selparser_error("invalid selection '%s'",
- _gmx_sel_lexer_pselstr(scanner));
- _gmx_sel_lexer_clear_method_stack(scanner);
- if (_gmx_sel_is_lexer_interactive(scanner))
- {
- _gmx_sel_lexer_clear_pselstr(scanner);
- yyerrok;
- }
- else
- {
- YYABORT;
- }
- }
-;
-
-/* Commands can be selections or variable assignments */
-cmd_plain: /* empty */
- {
- $$ = NULL;
- _gmx_sel_handle_empty_cmd(scanner);
- }
- | help_request { $$ = NULL; }
- | TOK_INT
- {
- t_selelem *s, *p;
- s = _gmx_sel_init_group_by_id($1, scanner);
- if (s == NULL) YYERROR;
- p = _gmx_sel_init_position(s, NULL, scanner);
- if (p == NULL) YYERROR;
- $$ = _gmx_sel_init_selection(strdup(s->name), p, scanner);
- }
- | string
- {
- t_selelem *s, *p;
- s = _gmx_sel_init_group_by_name($1, scanner);
- free($1);
- if (s == NULL) YYERROR;
- p = _gmx_sel_init_position(s, NULL, scanner);
- if (p == NULL) YYERROR;
- $$ = _gmx_sel_init_selection(strdup(s->name), p, scanner);
- }
- | selection
- { $$ = _gmx_sel_init_selection(NULL, $1, scanner); }
- | string selection
- { $$ = _gmx_sel_init_selection($1, $2, scanner); }
- | IDENTIFIER '=' sel_expr
- { $$ = _gmx_sel_assign_variable($1, $3, scanner); }
- | IDENTIFIER '=' num_expr
- { $$ = _gmx_sel_assign_variable($1, $3, scanner); }
- | IDENTIFIER '=' pos_expr
- { $$ = _gmx_sel_assign_variable($1, $3, scanner); }
-;
-
-/* Help requests */
-help_request:
- HELP { _gmx_sel_handle_help_cmd(NULL, scanner); }
- | help_topic
-;
-
-help_topic: HELP HELP_TOPIC { _gmx_sel_handle_help_cmd($2, scanner); }
- | help_topic HELP_TOPIC { _gmx_sel_handle_help_cmd($2, scanner); }
-;
-
-/* Selection is made of an expression and zero or more modifiers */
-selection: pos_expr { $$ = $1; }
- | sel_expr
- {
- $$ = _gmx_sel_init_position($1, NULL, scanner);
- if ($$ == NULL) YYERROR;
- }
- | '(' selection ')' { $$ = $2; }
- | selection MODIFIER method_params
- {
- $$ = _gmx_sel_init_modifier($2, $3, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/********************************************************************
- * BASIC NON-TERMINAL SYMBOLS
- ********************************************************************/
-
-integer_number:
- TOK_INT { $$ = $1; }
- | '-' TOK_INT { $$ = -$2; }
-;
-
-real_number:
- TOK_REAL { $$ = $1; }
- | '-' TOK_REAL { $$ = -$2; }
-;
-
-number: integer_number { $$ = $1; }
- | real_number { $$ = $1; }
-;
-
-string: STR { $$ = $1; }
- | IDENTIFIER { $$ = $1; }
-;
-
-/********************************************************************
- * ATOM SELECTION EXPRESSIONS
- ********************************************************************/
-
-/* Boolean expressions and grouping */
-sel_expr: NOT sel_expr
- {
- $$ = _gmx_selelem_create(SEL_BOOLEAN);
- $$->u.boolt = BOOL_NOT;
- $$->child = $2;
- }
- | sel_expr AND sel_expr
- {
- $$ = _gmx_selelem_create(SEL_BOOLEAN);
- $$->u.boolt = BOOL_AND;
- $$->child = $1; $$->child->next = $3;
- }
- | sel_expr OR sel_expr
- {
- $$ = _gmx_selelem_create(SEL_BOOLEAN);
- $$->u.boolt = BOOL_OR;
- $$->child = $1; $$->child->next = $3;
- }
-/* | sel_expr XOR sel_expr
- {
- $$ = _gmx_selelem_create(SEL_BOOLEAN);
- $$->u.boolt = BOOL_XOR;
- $$->child = $1; $$->child->next = $3;
- }*/
- | '(' sel_expr ')' { $$ = $2; }
-;
-
-/* Numeric comparisons */
-sel_expr: num_expr CMP_OP num_expr
- {
- $$ = _gmx_sel_init_comparison($1, $3, $2, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/* External groups */
-sel_expr: GROUP string
- {
- $$ = _gmx_sel_init_group_by_name($2, scanner);
- free($2);
- if ($$ == NULL) YYERROR;
- }
- | GROUP TOK_INT
- {
- $$ = _gmx_sel_init_group_by_id($2, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/* Position modifiers for selection methods */
-pos_mod: EMPTY_POSMOD { $$ = NULL; }
- | KEYWORD_POS { $$ = $1; }
-;
-
-/* Keyword selections */
-sel_expr: pos_mod KEYWORD_GROUP
- {
- $$ = _gmx_sel_init_keyword($2, NULL, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
- | pos_mod KEYWORD_STR basic_value_list
- {
- $$ = _gmx_sel_init_keyword($2, process_value_list($3, NULL), $1, scanner);
- if ($$ == NULL) YYERROR;
- }
- | pos_mod KEYWORD_NUMERIC basic_value_list
- {
- $$ = _gmx_sel_init_keyword($2, process_value_list($3, NULL), $1, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/* Custom selection methods */
-sel_expr: pos_mod METHOD_GROUP method_params
- {
- $$ = _gmx_sel_init_method($2, $3, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/********************************************************************
- * NUMERICAL EXPRESSIONS
- ********************************************************************/
-
-/* Basic numerical values */
-num_expr: TOK_INT
- {
- $$ = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype($$, INT_VALUE);
- _gmx_selvalue_reserve(&$$->v, 1);
- $$->v.u.i[0] = $1;
- }
- | TOK_REAL
- {
- $$ = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype($$, REAL_VALUE);
- _gmx_selvalue_reserve(&$$->v, 1);
- $$->v.u.r[0] = $1;
- }
-;
-
-/* Numeric selection methods */
-num_expr: pos_mod KEYWORD_NUMERIC %prec NUM_REDUCT
- {
- $$ = _gmx_sel_init_keyword($2, NULL, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
- | pos_mod METHOD_NUMERIC method_params
- {
- $$ = _gmx_sel_init_method($2, $3, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/* Arithmetic evaluation and grouping */
-num_expr: num_expr '+' num_expr
- { $$ = _gmx_sel_init_arithmetic($1, $3, '+', scanner); }
- | num_expr '-' num_expr
- { $$ = _gmx_sel_init_arithmetic($1, $3, '-', scanner); }
- | num_expr '*' num_expr
- { $$ = _gmx_sel_init_arithmetic($1, $3, '*', scanner); }
- | num_expr '/' num_expr
- { $$ = _gmx_sel_init_arithmetic($1, $3, '/', scanner); }
- | '-' num_expr %prec UNARY_NEG
- { $$ = _gmx_sel_init_arithmetic($2, NULL, '-', scanner); }
- | num_expr '^' num_expr
- { $$ = _gmx_sel_init_arithmetic($1, $3, '^', scanner); }
- | '(' num_expr ')' { $$ = $2; }
-;
-
-/********************************************************************
- * STRING EXPRESSIONS
- ********************************************************************/
-
-str_expr: string
- {
- $$ = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype($$, STR_VALUE);
- _gmx_selvalue_reserve(&$$->v, 1);
- $$->v.u.s[0] = $1;
- }
- | pos_mod KEYWORD_STR
- {
- $$ = _gmx_sel_init_keyword($2, NULL, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/********************************************************************
- * POSITION EXPRESSIONS
- ********************************************************************/
-
-/* Constant position expressions */
-pos_expr: '[' number ',' number ',' number ']'
- { $$ = _gmx_sel_init_const_position($2, $4, $6); }
-;
-
-/* Grouping of position expressions */
-pos_expr: '(' pos_expr ')' { $$ = $2; }
-;
-
-/* Expressions with a position value */
-pos_expr: METHOD_POS method_params
- {
- $$ = _gmx_sel_init_method($1, $2, NULL, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/* Evaluation of positions using a keyword */
-pos_expr: KEYWORD_POS OF sel_expr %prec PARAM_REDUCT
- {
- $$ = _gmx_sel_init_position($3, $1, scanner);
- if ($$ == NULL) YYERROR;
- }
-;
-
-/********************************************************************
- * VARIABLES
- ********************************************************************/
-
-sel_expr: VARIABLE_GROUP
- { $$ = _gmx_sel_init_variable_ref($1); }
-;
-
-num_expr: VARIABLE_NUMERIC
- { $$ = _gmx_sel_init_variable_ref($1); }
-;
-
-pos_expr: VARIABLE_POS
- { $$ = _gmx_sel_init_variable_ref($1); }
-;
-
-/********************************************************************
- * METHOD PARAMETERS
- ********************************************************************/
-
-method_params:
- method_param_list
- { $$ = process_param_list($1); }
- | method_param_list END_OF_METHOD
- { $$ = process_param_list($1); }
-;
-
-method_param_list:
- /* empty */ { $$ = NULL; }
- | method_param_list method_param
- { $2->next = $1; $$ = $2; }
-;
-
-method_param:
- PARAM value_list
- {
- $$ = _gmx_selexpr_create_param($1);
- $$->value = process_value_list($2, &$$->nval);
- }
-;
-
-value_list: /* empty */ { $$ = NULL; }
- | value_list_contents { $$ = $1; }
- | '{' value_list_contents '}' { $$ = $2; }
-;
-
-value_list_contents:
- value_item { $$ = $1; }
- | value_list_contents value_item
- { $2->next = $1; $$ = $2; }
- | value_list_contents ',' value_item
- { $3->next = $1; $$ = $3; }
-;
-
-basic_value_list:
- basic_value_list_contents { $$ = $1; }
- | '{' basic_value_list_contents '}' { $$ = $2; }
-;
-
-basic_value_list_contents:
- basic_value_item { $$ = $1; }
- | basic_value_list_contents basic_value_item
- { $2->next = $1; $$ = $2; }
- | basic_value_list_contents ',' basic_value_item
- { $3->next = $1; $$ = $3; }
-;
-
-value_item: sel_expr %prec PARAM_REDUCT
- { $$ = _gmx_selexpr_create_value_expr($1); }
- | pos_expr %prec PARAM_REDUCT
- { $$ = _gmx_selexpr_create_value_expr($1); }
- | num_expr %prec PARAM_REDUCT
- { $$ = _gmx_selexpr_create_value_expr($1); }
- | str_expr %prec PARAM_REDUCT
- { $$ = _gmx_selexpr_create_value_expr($1); }
- | value_item_range { $$ = $1; }
-;
-
-basic_value_item:
- integer_number %prec PARAM_REDUCT
- {
- $$ = _gmx_selexpr_create_value(INT_VALUE);
- $$->u.i.i1 = $$->u.i.i2 = $1;
- }
- | real_number %prec PARAM_REDUCT
- {
- $$ = _gmx_selexpr_create_value(REAL_VALUE);
- $$->u.r.r1 = $$->u.r.r2 = $1;
- }
- | string %prec PARAM_REDUCT
- {
- $$ = _gmx_selexpr_create_value(STR_VALUE);
- $$->u.s = $1;
- }
- | value_item_range { $$ = $1; }
-;
-
-value_item_range:
- integer_number TO integer_number
- {
- $$ = _gmx_selexpr_create_value(INT_VALUE);
- $$->u.i.i1 = $1; $$->u.i.i2 = $3;
- }
- | integer_number TO real_number
- {
- $$ = _gmx_selexpr_create_value(REAL_VALUE);
- $$->u.r.r1 = $1; $$->u.r.r2 = $3;
- }
- | real_number TO number
- {
- $$ = _gmx_selexpr_create_value(REAL_VALUE);
- $$->u.r.r1 = $1; $$->u.r.r2 = $3;
- }
-;
-
-%%
-
-static t_selexpr_value *
-process_value_list(t_selexpr_value *values, int *nr)
-{
- t_selexpr_value *val, *pval, *nval;
-
- /* Count values (if needed) and reverse list */
- if (nr)
- {
- *nr = 0;
- }
- pval = NULL;
- val = values;
- while (val)
- {
- if (nr)
- {
- ++*nr;
- }
- nval = val->next;
- val->next = pval;
- pval = val;
- val = nval;
- }
- values = pval;
-
- return values;
-}
-
-static t_selexpr_param *
-process_param_list(t_selexpr_param *params)
-{
- t_selexpr_param *par, *ppar, *npar;
-
- /* Reverse list */
- ppar = NULL;
- par = params;
- while (par)
- {
- npar = par->next;
- par->next = ppar;
- ppar = par;
- par = npar;
- }
- params = ppar;
-
- return params;
-}
-
-static void
-yyerror(yyscan_t scanner, char const *s)
-{
- _gmx_selparser_error("%s", s);
-}
-
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions of in parsetree.h.
- */
-/*! \internal
- * \page selparser Selection parsing
- *
- * The selection parser is implemented in the following files:
- * - scanner.l:
- * Tokenizer implemented using Flex, splits the input into tokens
- * (scanner.c and scanner_flex.h are generated from this file).
- * - scanner.h, scanner_internal.h, scanner_internal.c:
- * Helper functions for scanner.l and for interfacing between
- * scanner.l and parser.y. Functions in scanner_internal.h are only
- * used from scanner.l, while scanner.h is used from the parser.
- * - symrec.h, symrec.c:
- * Functions used by the tokenizer to handle the symbol table, i.e.,
- * the recognized keywords. Some basic keywords are hardcoded into
- * scanner.l, but all method and variable references go through the
- * symbol table, as do position evaluation keywords.
- * - parser.y:
- * Semantic rules for parsing the grammar
- * (parser.c and parser.h are generated from this file by Bison).
- * - parsetree.h, parsetree.c:
- * Functions called from actions in parser.y to construct the
- * evaluation elements corresponding to different grammar elements.
- * parsetree.c also defines the external interface of the parser,
- * i.e., the \c gmx_ana_selcollection_parse_*() functions declared
- * in selection.h.
- * - params.c:
- * Defines a function that processes the parameters of selection
- * methods and initializes the children of the method element.
- *
- * The basic control flow in the parser is as follows: when a
- * fmx_ana_selcollection_parse_*() gets called, it performs some
- * initialization, and then calls the _gmx_sel_yyparse() function generated
- * by Bison. This function then calls _gmx_sel_yylex() to repeatedly read
- * tokens from the input (more complex tasks related to token recognition
- * and bookkeeping are done by functions in scanner_internal.c) and uses the
- * grammar rules to decide what to do with them. Whenever a grammar rule
- * matches, a corresponding function in parsetree.c is called to construct
- * either a temporary representation for the object or a \c t_selelem object
- * (some simple rules are handled internally in parser.y).
- * When a complete selection has been parsed, the functions in parsetree.c
- * also take care of updating the \c gmx_ana_selcollection_t structure
- * appropriately.
- *
- * The rest of this page describes the resulting \c t_selelem object tree.
- * Before the selections can be evaluated, this tree needs to be passed to
- * the selection compiler, which is described on a separate page:
- * \ref selcompiler
- *
- *
- * \section selparser_tree Element tree constructed by the parser
- *
- * The parser initializes the following fields in all selection elements:
- * \c t_selelem::name, \c t_selelem::type, \c t_selelem::v\c .type,
- * \c t_selelem::flags, \c t_selelem::child, \c t_selelem::next, and
- * \c t_selelem::refcount.
- * Some other fields are also initialized for particular element types as
- * discussed below.
- * Fields that are not initialized are set to zero, NULL, or other similar
- * value.
- *
- *
- * \subsection selparser_tree_root Root elements
- *
- * The parser creates a \ref SEL_ROOT selection element for each variable
- * assignment and each selection. However, there are two exceptions that do
- * not result in a \ref SEL_ROOT element (in these cases, only the symbol
- * table is modified):
- * - Variable assignments that assign a variable to another variable.
- * - Variable assignments that assign a non-group constant.
- * .
- * The \ref SEL_ROOT elements are linked together in a chain in the same order
- * as in the input.
- *
- * The children of the \ref SEL_ROOT elements can be used to distinguish
- * the two types of root elements from each other:
- * - For variable assignments, the first and only child is always
- * a \ref SEL_SUBEXPR element.
- * - For selections, the first child is a \ref SEL_EXPRESSION or a
- * \ref SEL_MODIFIER element that evaluates the final positions (if the
- * selection defines a constant position, the child is a \ref SEL_CONST).
- * The rest of the children are \ref SEL_MODIFIER elements with
- * \ref NO_VALUE, in the order given by the user.
- * .
- * The name of the selection/variable is stored in \c t_selelem::cgrp\c .name.
- * It is set to either the name provided by the user or the selection string
- * for selections not explicitly named by the user.
- * \ref SEL_ROOT or \ref SEL_SUBEXPR elements do not appear anywhere else.
- *
- *
- * \subsection selparser_tree_const Constant elements
- *
- * \ref SEL_CONST elements are created for every constant that is required
- * for later evaluation.
- * Currently, \ref SEL_CONST elements can be present for
- * - selections that consist of a constant position,
- * - \ref GROUP_VALUE method parameters if provided using external index
- * groups,
- * .
- * For group-valued elements, the value is stored in \c t_selelem::cgrp;
- * other types of values are stored in \c t_selelem::v.
- * Constants that appear as parameters for selection methods are not present
- * in the selection tree unless they have \ref GROUP_VALUE.
- * \ref SEL_CONST elements have no children.
- *
- *
- * \subsection selparser_tree_method Method evaluation elements
- *
- * \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements are treated very
- * similarly. The \c gmx_ana_selmethod_t structure corresponding to the
- * evaluation method is in \c t_selelem::method, and the method data in
- * \c t_selelem::mdata has been allocated using sel_datafunc().
- * If a non-standard reference position type was set, \c t_selelem::pc has
- * also been created, but only the type has been set.
- * All children of these elements are of the type \ref SEL_SUBEXPRREF, and
- * each describes a selection that needs to be evaluated to obtain a value
- * for one parameter of the method.
- * No children are present for parameters that were given a constant
- * non-\ref GROUP_VALUE value.
- * The children are sorted in the order in which the parameters appear in the
- * \ref gmx_ana_selmethod_t structure.
- *
- * In addition to actual selection keywords, \ref SEL_EXPRESSION elements
- * are used internally to implement numerical comparisons (e.g., "x < 5")
- * and keyword matching (e.g., "resnr 1 to 3" or "name CA").
- *
- *
- * \subsection selparser_tree_subexpr Subexpression elements
- *
- * \ref SEL_SUBEXPR elements only appear for variables, as described above.
- * \c t_selelem::name points to the name of the variable (from the
- * \ref SEL_ROOT element).
- * The element always has exactly one child, which represents the value of
- * the variable.
- * \ref SEL_SUBEXPR element is the only element type that can have
- * \c t_selelem::refcount different from 1.
- *
- * \ref SEL_SUBEXPRREF elements are used for two purposes:
- * - Variable references that need to be evaluated (i.e., there is a
- * \ref SEL_SUBEXPR element for the variable) are represented using
- * \ref SEL_SUBEXPRREF elements.
- * In this case, \c t_selelem::param is NULL, and the first and only
- * child of the element is the \ref SEL_SUBEXPR element of the variable.
- * Such references can appear anywhere where the variable value
- * (the child of the \ref SEL_SUBEXPR element) would be valid.
- * - Children of \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements are
- * always of this type. For these elements, \c t_selelem::param is
- * initialized to point to the parameter that receives the value from
- * the expression.
- * Each such element has exactly one child, which can be of any type;
- * the \ref SEL_SUBEXPR element of a variable is used if the value comes
- * from a variable, otherwise the child type is not \ref SEL_SUBEXPR.
- *
- *
- * \subsection selparser_tree_gmx_bool Boolean elements
- *
- * One \ref SEL_BOOLEAN element is created for each gmx_boolean keyword in the
- * input, and the tree structure represents the evaluation order.
- * The \c t_selelem::boolt type gives the type of the operation.
- * Each element has exactly two children (one for \ref BOOL_NOT elements),
- * which are in the order given in the input.
- * The children always have \ref GROUP_VALUE, but different element types
- * are possible.
- *
- *
- * \subsection selparser_tree_arith Arithmetic elements
- *
- * One \ref SEL_ARITHMETIC element is created for each arithmetic operation in
- * the input, and the tree structure represents the evaluation order.
- * The \c t_selelem::optype type gives the name of the operation.
- * Each element has exactly two children (one for unary negation elements),
- * which are in the order given in the input.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#include <futil.h>
-#include <smalloc.h>
-#include <string2.h>
-#include <gmx_fatal.h>
-
-#include <poscalc.h>
-#include <selection.h>
-#include <selmethod.h>
-
-#include "keywords.h"
-#include "parsetree.h"
-#include "selcollection.h"
-#include "selelem.h"
-#include "selhelp.h"
-#include "symrec.h"
-
-#include "scanner.h"
-
-/* In parser.y */
-/*! \brief
- * Parser function generated by Bison.
- */
-int
-_gmx_sel_yybparse(void *scanner);
-
-/*!
- * It is a simple wrapper for fprintf(stderr, ...).
- */
-void
-_gmx_selparser_error(const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "selection parser: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
-}
-
-/*!
- * \param[in] type Type for the new value.
- * \returns Pointer to the newly allocated value.
- */
-t_selexpr_value *
-_gmx_selexpr_create_value(e_selvalue_t type)
-{
- t_selexpr_value *value;
- snew(value, 1);
- value->type = type;
- value->bExpr = FALSE;
- value->next = NULL;
- return value;
-}
-
-/*!
- * \param[in] expr Expression for the value.
- * \returns Pointer to the newly allocated value.
- */
-t_selexpr_value *
-_gmx_selexpr_create_value_expr(t_selelem *expr)
-{
- t_selexpr_value *value;
- snew(value, 1);
- value->type = expr->v.type;
- value->bExpr = TRUE;
- value->u.expr = expr;
- value->next = NULL;
- return value;
-}
-
-/*!
- * \param[in] name Name for the new parameter.
- * \returns Pointer to the newly allocated parameter.
- *
- * No copy of \p name is made.
- */
-t_selexpr_param *
-_gmx_selexpr_create_param(char *name)
-{
- t_selexpr_param *param;
- snew(param, 1);
- param->name = name;
- param->next = NULL;
- return param;
-}
-
-/*!
- * \param value Pointer to the beginning of the value list to free.
- *
- * The expressions referenced by the values are also freed
- * (to prevent this, set the expression to NULL before calling the function).
- */
-void
-_gmx_selexpr_free_values(t_selexpr_value *value)
-{
- t_selexpr_value *old;
-
- while (value)
- {
- if (value->bExpr)
- {
- if (value->u.expr)
- {
- _gmx_selelem_free(value->u.expr);
- }
- }
- else if (value->type == STR_VALUE)
- {
- sfree(value->u.s);
- }
- old = value;
- value = value->next;
- sfree(old);
- }
-}
-
-/*!
- * \param param Pointer the the beginning of the parameter list to free.
- *
- * The values of the parameters are freed with free_selexpr_values().
- */
-void
-_gmx_selexpr_free_params(t_selexpr_param *param)
-{
- t_selexpr_param *old;
-
- while (param)
- {
- _gmx_selexpr_free_values(param->value);
- old = param;
- param = param->next;
- sfree(old->name);
- sfree(old);
- }
-}
-
-/*!
- * \param[in,out] sel Root of the selection element tree to initialize.
- * \returns 0 on success, an error code on error.
- *
- * Propagates the \ref SEL_DYNAMIC flag from the children of \p sel to \p sel
- * (if any child of \p sel is dynamic, \p sel is also marked as such).
- * The \ref SEL_DYNAMIC flag is also set for \ref SEL_EXPRESSION elements with
- * a dynamic method.
- * Also, sets one of the \ref SEL_SINGLEVAL, \ref SEL_ATOMVAL, or
- * \ref SEL_VARNUMVAL flags, either based on the children or on the type of
- * the selection method.
- * If the types of the children conflict, an error is returned.
- *
- * The flags of the children of \p sel are also updated if not done earlier.
- * The flags are initialized only once for any element; if \ref SEL_FLAGSSET
- * is set for an element, the function returns immediately, and the recursive
- * operation does not descend beyond such elements.
- */
-int
-_gmx_selelem_update_flags(t_selelem *sel)
-{
- t_selelem *child;
- int rc;
- gmx_bool bUseChildType=FALSE;
- gmx_bool bOnlySingleChildren;
-
- /* Return if the flags have already been set */
- if (sel->flags & SEL_FLAGSSET)
- {
- return 0;
- }
- /* Set the flags based on the current element type */
- switch (sel->type)
- {
- case SEL_CONST:
- sel->flags |= SEL_SINGLEVAL;
- bUseChildType = FALSE;
- break;
-
- case SEL_EXPRESSION:
- if (sel->u.expr.method->flags & SMETH_DYNAMIC)
- {
- sel->flags |= SEL_DYNAMIC;
- }
- if (sel->u.expr.method->flags & SMETH_SINGLEVAL)
- {
- sel->flags |= SEL_SINGLEVAL;
- }
- else if (sel->u.expr.method->flags & SMETH_VARNUMVAL)
- {
- sel->flags |= SEL_VARNUMVAL;
- }
- else
- {
- sel->flags |= SEL_ATOMVAL;
- }
- bUseChildType = FALSE;
- break;
-
- case SEL_ARITHMETIC:
- sel->flags |= SEL_ATOMVAL;
- bUseChildType = FALSE;
- break;
-
- case SEL_MODIFIER:
- if (sel->v.type != NO_VALUE)
- {
- sel->flags |= SEL_VARNUMVAL;
- }
- bUseChildType = FALSE;
- break;
-
- case SEL_ROOT:
- bUseChildType = FALSE;
- break;
-
- case SEL_BOOLEAN:
- case SEL_SUBEXPR:
- case SEL_SUBEXPRREF:
- bUseChildType = TRUE;
- break;
- }
- /* Loop through children to propagate their flags upwards */
- bOnlySingleChildren = TRUE;
- child = sel->child;
- while (child)
- {
- /* Update the child */
- rc = _gmx_selelem_update_flags(child);
- if (rc != 0)
- {
- return rc;
- }
- /* Propagate the dynamic flag */
- sel->flags |= (child->flags & SEL_DYNAMIC);
- /* Propagate the type flag if necessary and check for problems */
- if (bUseChildType)
- {
- if ((sel->flags & SEL_VALTYPEMASK)
- && !(sel->flags & child->flags & SEL_VALTYPEMASK))
- {
- _gmx_selparser_error("invalid combination of selection expressions");
- return EINVAL;
- }
- sel->flags |= (child->flags & SEL_VALTYPEMASK);
- }
- if (!(child->flags & SEL_SINGLEVAL))
- {
- bOnlySingleChildren = FALSE;
- }
-
- child = child->next;
- }
- /* For arithmetic expressions consisting only of single values,
- * the result is also a single value. */
- if (sel->type == SEL_ARITHMETIC && bOnlySingleChildren)
- {
- sel->flags = (sel->flags & ~SEL_VALTYPEMASK) | SEL_SINGLEVAL;
- }
- /* For root elements, the type should be propagated here, after the
- * children have been updated. */
- if (sel->type == SEL_ROOT)
- {
- sel->flags |= (sel->child->flags & SEL_VALTYPEMASK);
- }
- /* Mark that the flags are set */
- sel->flags |= SEL_FLAGSSET;
- return 0;
-}
-
-/*!
- * \param[in,out] sel Selection element to initialize.
- * \param[in] scanner Scanner data structure.
- *
- * A deep copy of the parameters is made to allow several
- * expressions with the same method to coexist peacefully.
- * Calls sel_datafunc() if one is specified for the method.
- */
-void
-_gmx_selelem_init_method_params(t_selelem *sel, yyscan_t scanner)
-{
- int nparams;
- gmx_ana_selparam_t *orgparam;
- gmx_ana_selparam_t *param;
- int i;
- void *mdata;
-
- nparams = sel->u.expr.method->nparams;
- orgparam = sel->u.expr.method->param;
- snew(param, nparams);
- memcpy(param, orgparam, nparams*sizeof(gmx_ana_selparam_t));
- for (i = 0; i < nparams; ++i)
- {
- param[i].flags &= ~SPAR_SET;
- _gmx_selvalue_setstore(¶m[i].val, NULL);
- if (param[i].flags & SPAR_VARNUM)
- {
- param[i].val.nr = -1;
- }
- /* Duplicate the enum value array if it is given statically */
- if ((param[i].flags & SPAR_ENUMVAL) && orgparam[i].val.u.ptr != NULL)
- {
- int n;
-
- /* Count the values */
- n = 1;
- while (orgparam[i].val.u.s[n] != NULL)
- {
- ++n;
- }
- _gmx_selvalue_reserve(¶m[i].val, n+1);
- memcpy(param[i].val.u.s, orgparam[i].val.u.s,
- (n+1)*sizeof(param[i].val.u.s[0]));
- }
- }
- mdata = NULL;
- if (sel->u.expr.method->init_data)
- {
- mdata = sel->u.expr.method->init_data(nparams, param);
- if (mdata == NULL)
- {
- gmx_fatal(FARGS, "Method data initialization failed");
- }
- }
- if (sel->u.expr.method->set_poscoll)
- {
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
-
- sel->u.expr.method->set_poscoll(sc->pcc, mdata);
- }
- /* Store the values */
- sel->u.expr.method->param = param;
- sel->u.expr.mdata = mdata;
-}
-
-/*!
- * \param[in,out] sel Selection element to initialize.
- * \param[in] method Selection method to set.
- * \param[in] scanner Scanner data structure.
- *
- * Makes a copy of \p method and stores it in \p sel->u.expr.method,
- * and calls _gmx_selelem_init_method_params();
- */
-void
-_gmx_selelem_set_method(t_selelem *sel, gmx_ana_selmethod_t *method,
- yyscan_t scanner)
-{
- int i;
-
- _gmx_selelem_set_vtype(sel, method->type);
- sel->name = method->name;
- snew(sel->u.expr.method, 1);
- memcpy(sel->u.expr.method, method, sizeof(gmx_ana_selmethod_t));
- _gmx_selelem_init_method_params(sel, scanner);
-}
-
-/*! \brief
- * Initializes the reference position calculation for a \ref SEL_EXPRESSION
- * element.
- *
- * \param[in,out] pcc Position calculation collection to use.
- * \param[in,out] sel Selection element to initialize.
- * \param[in] rpost Reference position type to use (NULL = default).
- * \returns 0 on success, a non-zero error code on error.
- */
-static int
-set_refpos_type(gmx_ana_poscalc_coll_t *pcc, t_selelem *sel, const char *rpost)
-{
- int rc;
-
- if (!rpost)
- {
- return 0;
- }
-
- rc = 0;
- if (sel->u.expr.method->pupdate)
- {
- /* By default, use whole residues/molecules. */
- rc = gmx_ana_poscalc_create_enum(&sel->u.expr.pc, pcc, rpost,
- POS_COMPLWHOLE);
- }
- else
- {
- _gmx_selparser_error("warning: '%s' modifier for '%s' ignored",
- rpost, sel->u.expr.method->name);
- }
- return rc;
-}
-
-/*!
- * \param[in] left Selection element for the left hand side.
- * \param[in] right Selection element for the right hand side.
- * \param[in] op String representation of the operator.
- * \param[in] scanner Scanner data structure.
- * \returns The created selection element.
- *
- * This function handles the creation of a \c t_selelem object for
- * arithmetic expressions.
- */
-t_selelem *
-_gmx_sel_init_arithmetic(t_selelem *left, t_selelem *right, char op,
- yyscan_t scanner)
-{
- t_selelem *sel;
- char buf[2];
-
- buf[0] = op;
- buf[1] = 0;
- sel = _gmx_selelem_create(SEL_ARITHMETIC);
- sel->v.type = REAL_VALUE;
- switch(op)
- {
- case '+': sel->u.arith.type = ARITH_PLUS; break;
- case '-': sel->u.arith.type = (right ? ARITH_MINUS : ARITH_NEG); break;
- case '*': sel->u.arith.type = ARITH_MULT; break;
- case '/': sel->u.arith.type = ARITH_DIV; break;
- case '^': sel->u.arith.type = ARITH_EXP; break;
- }
- sel->u.arith.opstr = strdup(buf);
- sel->name = sel->u.arith.opstr;
- sel->child = left;
- sel->child->next = right;
- return sel;
-}
-
-/*!
- * \param[in] left Selection element for the left hand side.
- * \param[in] right Selection element for the right hand side.
- * \param[in] cmpop String representation of the comparison operator.
- * \param[in] scanner Scanner data structure.
- * \returns The created selection element.
- *
- * This function handles the creation of a \c t_selelem object for
- * comparison expressions.
- */
-t_selelem *
-_gmx_sel_init_comparison(t_selelem *left, t_selelem *right, char *cmpop,
- yyscan_t scanner)
-{
- t_selelem *sel;
- t_selexpr_param *params, *param;
- const char *name;
- int rc;
-
- sel = _gmx_selelem_create(SEL_EXPRESSION);
- _gmx_selelem_set_method(sel, &sm_compare, scanner);
- /* Create the parameter for the left expression */
- name = left->v.type == INT_VALUE ? "int1" : "real1";
- params = param = _gmx_selexpr_create_param(strdup(name));
- param->nval = 1;
- param->value = _gmx_selexpr_create_value_expr(left);
- /* Create the parameter for the right expression */
- name = right->v.type == INT_VALUE ? "int2" : "real2";
- param = _gmx_selexpr_create_param(strdup(name));
- param->nval = 1;
- param->value = _gmx_selexpr_create_value_expr(right);
- params->next = param;
- /* Create the parameter for the operator */
- param = _gmx_selexpr_create_param(strdup("op"));
- param->nval = 1;
- param->value = _gmx_selexpr_create_value(STR_VALUE);
- param->value->u.s = cmpop;
- params->next->next = param;
- if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams,
- sel->u.expr.method->param, sel, scanner))
- {
- _gmx_selparser_error("error in comparison initialization");
- _gmx_selelem_free(sel);
- return NULL;
- }
-
- return sel;
-}
-
-/*!
- * \param[in] method Method to use.
- * \param[in] args Pointer to the first argument.
- * \param[in] rpost Reference position type to use (NULL = default).
- * \param[in] scanner Scanner data structure.
- * \returns The created selection element.
- *
- * This function handles the creation of a \c t_selelem object for
- * selection methods that do not take parameters.
- */
-t_selelem *
-_gmx_sel_init_keyword(gmx_ana_selmethod_t *method, t_selexpr_value *args,
- const char *rpost, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- t_selelem *root, *child;
- t_selexpr_param *params, *param;
- t_selexpr_value *arg;
- int nargs;
- int rc;
-
- if (method->nparams > 0)
- {
- gmx_bug("internal error");
- return NULL;
- }
-
- root = _gmx_selelem_create(SEL_EXPRESSION);
- child = root;
- _gmx_selelem_set_method(child, method, scanner);
-
- /* Initialize the evaluation of keyword matching if values are provided */
- if (args)
- {
- gmx_ana_selmethod_t *kwmethod;
- switch (method->type)
- {
- case INT_VALUE: kwmethod = &sm_keyword_int; break;
- case REAL_VALUE: kwmethod = &sm_keyword_real; break;
- case STR_VALUE: kwmethod = &sm_keyword_str; break;
- default:
- _gmx_selparser_error("unknown type for keyword selection");
- _gmx_selexpr_free_values(args);
- goto on_error;
- }
- /* Count the arguments */
- nargs = 0;
- arg = args;
- while (arg)
- {
- ++nargs;
- arg = arg->next;
- }
- /* Initialize the selection element */
- root = _gmx_selelem_create(SEL_EXPRESSION);
- _gmx_selelem_set_method(root, kwmethod, scanner);
- params = param = _gmx_selexpr_create_param(NULL);
- param->nval = 1;
- param->value = _gmx_selexpr_create_value_expr(child);
- param = _gmx_selexpr_create_param(NULL);
- param->nval = nargs;
- param->value = args;
- params->next = param;
- if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
- root->u.expr.method->param, root, scanner))
- {
- _gmx_selparser_error("error in keyword selection initialization");
- goto on_error;
- }
- }
- rc = set_refpos_type(sc->pcc, child, rpost);
- if (rc != 0)
- {
- goto on_error;
- }
-
- return root;
-
-/* On error, free all memory and return NULL. */
-on_error:
- _gmx_selelem_free(root);
- return NULL;
-}
-
-/*!
- * \param[in] method Method to use for initialization.
- * \param[in] params Pointer to the first parameter.
- * \param[in] rpost Reference position type to use (NULL = default).
- * \param[in] scanner Scanner data structure.
- * \returns The created selection element.
- *
- * This function handles the creation of a \c t_selelem object for
- * selection methods that take parameters.
- *
- * Part of the behavior of the \c same selection keyword is hardcoded into
- * this function (or rather, into _gmx_selelem_custom_init_same()) to allow the
- * use of any keyword in \c "same KEYWORD as" without requiring special
- * handling somewhere else (or sacrificing the simple syntax).
- */
-t_selelem *
-_gmx_sel_init_method(gmx_ana_selmethod_t *method, t_selexpr_param *params,
- const char *rpost, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- t_selelem *root;
- int rc;
-
- _gmx_sel_finish_method(scanner);
- /* The "same" keyword needs some custom massaging of the parameters. */
- rc = _gmx_selelem_custom_init_same(&method, params, scanner);
- if (rc != 0)
- {
- _gmx_selexpr_free_params(params);
- return NULL;
- }
- root = _gmx_selelem_create(SEL_EXPRESSION);
- _gmx_selelem_set_method(root, method, scanner);
- /* Process the parameters */
- if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
- root->u.expr.method->param, root, scanner))
- {
- _gmx_selelem_free(root);
- return NULL;
- }
- rc = set_refpos_type(sc->pcc, root, rpost);
- if (rc != 0)
- {
- _gmx_selelem_free(root);
- return NULL;
- }
-
- return root;
-}
-
-/*!
- * \param[in] method Modifier to use for initialization.
- * \param[in] params Pointer to the first parameter.
- * \param[in] sel Selection element that the modifier should act on.
- * \param[in] scanner Scanner data structure.
- * \returns The created selection element.
- *
- * This function handles the creation of a \c t_selelem object for
- * selection modifiers.
- */
-t_selelem *
-_gmx_sel_init_modifier(gmx_ana_selmethod_t *method, t_selexpr_param *params,
- t_selelem *sel, yyscan_t scanner)
-{
- t_selelem *root;
- t_selelem *mod;
- t_selexpr_param *vparam;
- int i;
-
- _gmx_sel_finish_method(scanner);
- mod = _gmx_selelem_create(SEL_MODIFIER);
- _gmx_selelem_set_method(mod, method, scanner);
- if (method->type == NO_VALUE)
- {
- t_selelem *child;
-
- child = sel;
- while (child->next)
- {
- child = child->next;
- }
- child->next = mod;
- root = sel;
- }
- else
- {
- vparam = _gmx_selexpr_create_param(NULL);
- vparam->nval = 1;
- vparam->value = _gmx_selexpr_create_value_expr(sel);
- vparam->next = params;
- params = vparam;
- root = mod;
- }
- /* Process the parameters */
- if (!_gmx_sel_parse_params(params, mod->u.expr.method->nparams,
- mod->u.expr.method->param, mod, scanner))
- {
- _gmx_selelem_free(mod);
- return NULL;
- }
-
- return root;
-}
-
-/*!
- * \param[in] expr Input selection element for the position calculation.
- * \param[in] type Reference position type or NULL for default.
- * \param[in] scanner Scanner data structure.
- * \returns The created selection element.
- *
- * This function handles the creation of a \c t_selelem object for
- * evaluation of reference positions.
- */
-t_selelem *
-_gmx_sel_init_position(t_selelem *expr, const char *type, yyscan_t scanner)
-{
- t_selelem *root;
- t_selexpr_param *params;
-
- root = _gmx_selelem_create(SEL_EXPRESSION);
- _gmx_selelem_set_method(root, &sm_keyword_pos, scanner);
- _gmx_selelem_set_kwpos_type(root, type);
- /* Create the parameters for the parameter parser. */
- params = _gmx_selexpr_create_param(NULL);
- params->nval = 1;
- params->value = _gmx_selexpr_create_value_expr(expr);
- /* Parse the parameters. */
- if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
- root->u.expr.method->param, root, scanner))
- {
- _gmx_selelem_free(root);
- return NULL;
- }
-
- return root;
-}
-
-/*!
- * \param[in] x,y,z Coordinates for the position.
- * \returns The creates selection element.
- */
-t_selelem *
-_gmx_sel_init_const_position(real x, real y, real z)
-{
- t_selelem *sel;
- rvec pos;
-
- sel = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype(sel, POS_VALUE);
- _gmx_selvalue_reserve(&sel->v, 1);
- pos[XX] = x;
- pos[YY] = y;
- pos[ZZ] = z;
- gmx_ana_pos_init_const(sel->v.u.p, pos);
- return sel;
-}
-
-/*!
- * \param[in] name Name of an index group to search for.
- * \param[in] scanner Scanner data structure.
- * \returns The created constant selection element, or NULL if no matching
- * index group found.
- *
- * See gmx_ana_indexgrps_find() for information on how \p name is matched
- * against the index groups.
- */
-t_selelem *
-_gmx_sel_init_group_by_name(const char *name, yyscan_t scanner)
-{
- gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
- t_selelem *sel;
-
- if (!grps)
- {
- return NULL;
- }
- sel = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype(sel, GROUP_VALUE);
- /* FIXME: The constness should not be cast away */
- if (!gmx_ana_indexgrps_find(&sel->u.cgrp, grps, (char *)name))
- {
- _gmx_selelem_free(sel);
- return NULL;
- }
- sel->name = sel->u.cgrp.name;
- return sel;
-}
-
-/*!
- * \param[in] id Zero-based index number of the group to extract.
- * \param[in] scanner Scanner data structure.
- * \returns The created constant selection element, or NULL if no matching
- * index group found.
- */
-t_selelem *
-_gmx_sel_init_group_by_id(int id, yyscan_t scanner)
-{
- gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
- t_selelem *sel;
-
- if (!grps)
- {
- return NULL;
- }
- sel = _gmx_selelem_create(SEL_CONST);
- _gmx_selelem_set_vtype(sel, GROUP_VALUE);
- if (!gmx_ana_indexgrps_extract(&sel->u.cgrp, grps, id))
- {
- _gmx_selelem_free(sel);
- return NULL;
- }
- sel->name = sel->u.cgrp.name;
- return sel;
-}
-
-/*!
- * \param[in,out] sel Value of the variable.
- * \returns The created selection element that references \p sel.
- *
- * The reference count of \p sel is updated, but no other modifications are
- * made.
- */
-t_selelem *
-_gmx_sel_init_variable_ref(t_selelem *sel)
-{
- t_selelem *ref;
-
- if (sel->v.type == POS_VALUE && sel->type == SEL_CONST)
- {
- ref = sel;
- }
- else
- {
- ref = _gmx_selelem_create(SEL_SUBEXPRREF);
- _gmx_selelem_set_vtype(ref, sel->v.type);
- ref->name = sel->name;
- ref->child = sel;
- }
- sel->refcount++;
- return ref;
-}
-
-/*! \brief
- * Initializes default values for position keyword evaluation.
- *
- * \param[in,out] root Root of the element tree to initialize.
- * \param[in] sc Selection collection to use defaults from.
- * \param[in] bSelection Whether the element evaluates the positions for a
- * selection.
- */
-static void
-init_pos_keyword_defaults(t_selelem *root, gmx_ana_selcollection_t *sc, gmx_bool bSelection)
-{
- t_selelem *child;
- int flags;
-
- /* Selections use largest static group by default, while
- * reference positions use the whole residue/molecule. */
- if (root->type == SEL_EXPRESSION)
- {
- flags = bSelection ? POS_COMPLMAX : POS_COMPLWHOLE;
- if (bSelection && sc->bMaskOnly)
- {
- flags |= POS_MASKONLY;
- }
- if (bSelection && sc->bVelocities)
- {
- flags |= POS_VELOCITIES;
- }
- if (bSelection && sc->bForces)
- {
- flags |= POS_FORCES;
- }
- _gmx_selelem_set_kwpos_type(root, bSelection ? sc->spost : sc->rpost);
- _gmx_selelem_set_kwpos_flags(root, flags);
- }
- /* Change the defaults once we are no longer processing modifiers */
- if (root->type != SEL_ROOT && root->type != SEL_MODIFIER
- && root->type != SEL_SUBEXPRREF && root->type != SEL_SUBEXPR)
- {
- bSelection = FALSE;
- }
- /* Recurse into children */
- child = root->child;
- while (child)
- {
- init_pos_keyword_defaults(child, sc, bSelection);
- child = child->next;
- }
-}
-
-/*!
- * \param[in] name Name for the selection
- * (if NULL, a default name is constructed).
- * \param[in] sel The selection element that evaluates the selection.
- * \param scanner Scanner data structure.
- * \returns The created root selection element.
- *
- * This function handles the creation of root (\ref SEL_ROOT) \c t_selelem
- * objects for selections.
- */
-t_selelem *
-_gmx_sel_init_selection(char *name, t_selelem *sel, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- t_selelem *root;
- int rc;
-
- if (sel->v.type != POS_VALUE)
- {
- gmx_bug("each selection must evaluate to a position");
- /* FIXME: Better handling of this error */
- sfree(name);
- return NULL;
- }
-
- root = _gmx_selelem_create(SEL_ROOT);
- root->child = sel;
- /* Assign the name (this is done here to free it automatically in the case
- * of an error below). */
- if (name)
- {
- root->name = root->u.cgrp.name = name;
- }
- /* Update the flags */
- rc = _gmx_selelem_update_flags(root);
- if (rc != 0)
- {
- _gmx_selelem_free(root);
- return NULL;
- }
- /* Initialize defaults for position keywords */
- init_pos_keyword_defaults(sel, sc, TRUE);
-
- /* If there is no name provided by the user, check whether the actual
- * selection given was from an external group, and if so, use the name
- * of the external group. */
- if (!root->name)
- {
- t_selelem *child = root->child;
- while (child->type == SEL_MODIFIER)
- {
- if (!child->child || child->child->type != SEL_SUBEXPRREF
- || !child->child->child)
- {
- break;
- }
- child = child->child->child;
- }
- if (child->type == SEL_EXPRESSION
- && child->child && child->child->type == SEL_SUBEXPRREF
- && child->child->child
- && child->child->child->type == SEL_CONST
- && child->child->child->v.type == GROUP_VALUE)
- {
- root->name = root->u.cgrp.name =
- strdup(child->child->child->u.cgrp.name);
- }
- }
- /* If there still is no name, use the selection string */
- if (!root->name)
- {
- root->name = root->u.cgrp.name
- = strdup(_gmx_sel_lexer_pselstr(scanner));
- }
-
- /* Print out some information if the parser is interactive */
- if (_gmx_sel_is_lexer_interactive(scanner))
- {
- fprintf(stderr, "Selection '%s' parsed\n",
- _gmx_sel_lexer_pselstr(scanner));
- }
-
- return root;
-}
-
-
-/*!
- * \param[in] name Name of the variable (should not be freed after this
- * function).
- * \param[in] expr The selection element that evaluates the variable.
- * \param scanner Scanner data structure.
- * \returns The created root selection element.
- *
- * This function handles the creation of root \c t_selelem objects for
- * variable assignments. A \ref SEL_ROOT element and a \ref SEL_SUBEXPR
- * element are both created.
- */
-t_selelem *
-_gmx_sel_assign_variable(char *name, t_selelem *expr, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- const char *pselstr = _gmx_sel_lexer_pselstr(scanner);
- t_selelem *root = NULL;
- int rc;
-
- rc = _gmx_selelem_update_flags(expr);
- if (rc != 0)
- {
- sfree(name);
- _gmx_selelem_free(expr);
- return NULL;
- }
- /* Check if this is a constant non-group value */
- if (expr->type == SEL_CONST && expr->v.type != GROUP_VALUE)
- {
- /* If so, just assign the constant value to the variable */
- if (!_gmx_sel_add_var_symbol(sc->symtab, name, expr))
- {
- _gmx_selelem_free(expr);
- sfree(name);
- return NULL;
- }
- _gmx_selelem_free(expr);
- sfree(name);
- goto finish;
- }
- /* Check if we are assigning a variable to another variable */
- if (expr->type == SEL_SUBEXPRREF)
- {
- /* If so, make a simple alias */
- if (!_gmx_sel_add_var_symbol(sc->symtab, name, expr->child))
- {
- _gmx_selelem_free(expr);
- sfree(name);
- return NULL;
- }
- _gmx_selelem_free(expr);
- sfree(name);
- goto finish;
- }
- /* Create the root element */
- root = _gmx_selelem_create(SEL_ROOT);
- root->name = name;
- root->u.cgrp.name = name;
- /* Create the subexpression element */
- root->child = _gmx_selelem_create(SEL_SUBEXPR);
- _gmx_selelem_set_vtype(root->child, expr->v.type);
- root->child->name = name;
- root->child->child = expr;
- /* Update flags */
- rc = _gmx_selelem_update_flags(root);
- if (rc != 0)
- {
- _gmx_selelem_free(root);
- return NULL;
- }
- /* Add the variable to the symbol table */
- if (!_gmx_sel_add_var_symbol(sc->symtab, name, root->child))
- {
- _gmx_selelem_free(root);
- return NULL;
- }
-finish:
- srenew(sc->varstrs, sc->nvars + 1);
- sc->varstrs[sc->nvars] = strdup(pselstr);
- ++sc->nvars;
- if (_gmx_sel_is_lexer_interactive(scanner))
- {
- fprintf(stderr, "Variable '%s' parsed\n", pselstr);
- }
- return root;
-}
-
-/*!
- * \param sel Selection to append (can be NULL, in which
- * case nothing is done).
- * \param last Last selection, or NULL if not present or not known.
- * \param scanner Scanner data structure.
- * \returns The last selection after the append.
- *
- * Appends \p sel after the last root element, and returns either \p sel
- * (if it was non-NULL) or the last element (if \p sel was NULL).
- */
-t_selelem *
-_gmx_sel_append_selection(t_selelem *sel, t_selelem *last, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
-
- /* Append sel after last, or the last element of sc if last is NULL */
- if (last)
- {
- last->next = sel;
- }
- else
- {
- if (sc->root)
- {
- last = sc->root;
- while (last->next)
- {
- last = last->next;
- }
- last->next = sel;
- }
- else
- {
- sc->root = sel;
- }
- }
- /* Initialize a selection object if necessary */
- if (sel)
- {
- last = sel;
- /* Add the new selection to the collection if it is not a variable. */
- if (sel->child->type != SEL_SUBEXPR)
- {
- int i;
-
- sc->nr++;
- srenew(sc->sel, sc->nr);
- i = sc->nr - 1;
- snew(sc->sel[i], 1);
- sc->sel[i]->name = strdup(sel->name);
- sc->sel[i]->selstr = strdup(_gmx_sel_lexer_pselstr(scanner));
-
- if (sel->child->type == SEL_CONST)
- {
- gmx_ana_pos_copy(&sc->sel[i]->p, sel->child->v.u.p, TRUE);
- sc->sel[i]->bDynamic = FALSE;
- }
- else
- {
- t_selelem *child;
-
- child = sel->child;
- child->flags &= ~SEL_ALLOCVAL;
- _gmx_selvalue_setstore(&child->v, &sc->sel[i]->p);
- /* We should also skip any modifiers to determine the dynamic
- * status. */
- while (child->type == SEL_MODIFIER)
- {
- child = child->child;
- if (child->type == SEL_SUBEXPRREF)
- {
- child = child->child;
- /* Because most subexpression elements are created
- * during compilation, we need to check for them
- * explicitly here.
- */
- if (child->type == SEL_SUBEXPR)
- {
- child = child->child;
- }
- }
- }
- /* For variable references, we should skip the
- * SEL_SUBEXPRREF and SEL_SUBEXPR elements. */
- if (child->type == SEL_SUBEXPRREF)
- {
- child = child->child->child;
- }
- sc->sel[i]->bDynamic = (child->child->flags & SEL_DYNAMIC);
- }
- /* The group will be set after compilation */
- sc->sel[i]->g = NULL;
- sc->sel[i]->selelem = sel;
- gmx_ana_selection_init_coverfrac(sc->sel[i], CFRAC_NONE);
- }
- }
- /* Clear the selection string now that we've saved it */
- _gmx_sel_lexer_clear_pselstr(scanner);
- return last;
-}
-
-/*!
- * \param[in] scanner Scanner data structure.
- * \returns TRUE if the parser should finish, FALSE if parsing should
- * continue.
- *
- * This function is called always after _gmx_sel_append_selection() to
- * check whether a sufficient number of selections has already been provided.
- * This is used to terminate interactive parsers when the correct number of
- * selections has been provided.
- */
-gmx_bool
-_gmx_sel_parser_should_finish(yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- return sc->nr == _gmx_sel_lexer_exp_selcount(scanner);
-}
-
-/*!
- * \param[in] scanner Scanner data structure.
- */
-void
-_gmx_sel_handle_empty_cmd(yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
- int i;
-
- if (!_gmx_sel_is_lexer_interactive(scanner))
- return;
-
- if (grps)
- {
- fprintf(stderr, "Available index groups:\n");
- gmx_ana_indexgrps_print(_gmx_sel_lexer_indexgrps(scanner), 0);
- }
- if (sc->nvars > 0 || sc->nr > 0)
- {
- fprintf(stderr, "Currently provided selections:\n");
- for (i = 0; i < sc->nvars; ++i)
- {
- fprintf(stderr, " %s\n", sc->varstrs[i]);
- }
- for (i = 0; i < sc->nr; ++i)
- {
- fprintf(stderr, " %2d. %s\n", i+1, sc->sel[i]->selstr);
- }
- }
-}
-
-/*!
- * \param[in] topic Topic for which help was requested, or NULL for general
- * help.
- * \param[in] scanner Scanner data structure.
- *
- * \p topic is freed by this function.
- */
-void
-_gmx_sel_handle_help_cmd(char *topic, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
-
- _gmx_sel_print_help(sc, topic);
- if (topic)
- {
- sfree(topic);
- }
-}
-
-/*! \brief
- * Internal helper function used by gmx_ana_selcollection_parse_*() to do the actual work.
- *
- * \param[in] maxnr Maximum number of selections to parse
- * (if -1, parse as many as provided by the user).
- * \param[in,out] scanner Scanner data structure.
- * \returns 0 on success, -1 on error.
- */
-static int
-run_parser(int maxnr, yyscan_t scanner)
-{
- gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
- gmx_bool bOk;
- int nr;
-
- nr = sc->nr;
- bOk = !_gmx_sel_yybparse(scanner);
- _gmx_sel_free_lexer(scanner);
- nr = sc->nr - nr;
- if (maxnr > 0 && nr != maxnr)
- {
- return -1;
- }
- return bOk ? 0 : -1;
-}
-
-/*!
- * \param[in,out] sc Selection collection to use for output.
- * \param[in] nr Number of selections to parse
- * (if -1, parse as many as provided by the user).
- * \param[in] grps External index groups (can be NULL).
- * \param[in] bInteractive Whether the parser should behave interactively.
- * \returns 0 on success, -1 on error.
- *
- * The number of selections parsed can be accessed with
- * gmx_ana_selcollection_get_count() (note that if you call the parser
- * multiple times, this function returns the total count).
- */
-int
-gmx_ana_selcollection_parse_stdin(gmx_ana_selcollection_t *sc, int nr,
- gmx_ana_indexgrps_t *grps, gmx_bool bInteractive)
-{
- yyscan_t scanner;
- int rc;
-
- rc = _gmx_sel_init_lexer(&scanner, sc, bInteractive, nr, grps);
- if (rc != 0)
- {
- return rc;
- }
- /* We don't set the lexer input here, which causes it to use a special
- * internal implementation for reading from stdin. */
- return run_parser(nr, scanner);
-}
-
-/*!
- * \param[in,out] sc Selection collection to use for output.
- * \param[in] fnm Name of the file to parse selections from.
- * \param[in] grps External index groups (can be NULL).
- * \returns 0 on success, -1 on error.
- *
- * The number of selections parsed can be accessed with
- * gmx_ana_selcollection_get_count() (note that if you call the parser
- * multiple times, this function returns the total count).
- */
-int
-gmx_ana_selcollection_parse_file(gmx_ana_selcollection_t *sc, const char *fnm,
- gmx_ana_indexgrps_t *grps)
-{
- yyscan_t scanner;
- FILE *fp;
- int rc;
-
- rc = _gmx_sel_init_lexer(&scanner, sc, FALSE, -1, grps);
- if (rc != 0)
- {
- return rc;
- }
- fp = ffopen(fnm, "r");
- _gmx_sel_set_lex_input_file(scanner, fp);
- rc = run_parser(-1, scanner);
- ffclose(fp);
- return rc;
-}
-
-/*!
- * \param[in,out] sc Selection collection to use for output.
- * \param[in] str String to parse selections from.
- * \param[in] grps External index groups (can be NULL).
- * \returns 0 on success, -1 on error.
- *
- * The number of selections parsed can be accessed with
- * gmx_ana_selcollection_get_count() (note that if you call the parser
- * multiple times, this function returns the total count).
- */
-int
-gmx_ana_selcollection_parse_str(gmx_ana_selcollection_t *sc, const char *str,
- gmx_ana_indexgrps_t *grps)
-{
- yyscan_t scanner;
- int rc;
-
- rc = _gmx_sel_init_lexer(&scanner, sc, FALSE, -1, grps);
- if (rc != 0)
- {
- return rc;
- }
- _gmx_sel_set_lex_input_str(scanner, str);
- return run_parser(-1, scanner);
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Handling of intermediate selection parser data.
- *
- * The data types declared in this header are used by the parser to store
- * intermediate data when constructing method expressions.
- * In particular, the parameters for the method are stored.
- * The intermediate data is freed once a \c t_selelem object can be
- * constructed.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_PARSETREE_H
-#define SELECTION_PARSETREE_H
-
-/*#include <typedefs.h>*/
-#include <types/simple.h>
-
-
-#include <selvalue.h>
-
-struct t_selelem;
-struct gmx_ana_indexgrps_t;
-struct gmx_ana_selmethod_t;
-struct gmx_ana_selparam_t;
-
-/*! \internal \brief
- * Describes a parsed value, possibly resulting from expression evaluation.
- */
-typedef struct t_selexpr_value
-{
- /** Type of the value. */
- e_selvalue_t type;
- /** TRUE if the value is the result of an expression. */
- gmx_bool bExpr;
- union {
- /** The integer value/range (\p type INT_VALUE); */
- struct {
- /** Beginning of the range. */
- int i1;
- /** End of the range; equals \p i1 for a single integer. */
- int i2;
- } i;
- /** The real value/range (\p type REAL_VALUE); */
- struct {
- /** Beginning of the range. */
- real r1;
- /** End of the range; equals \p r1 for a single number. */
- real r2;
- } r;
- /** The string value (\p type STR_VALUE); */
- char *s;
- /** The position value (\p type POS_VALUE); */
- rvec x;
- /** The expression if \p bExpr is TRUE. */
- struct t_selelem *expr;
- } u;
- /** Pointer to the next value. */
- struct t_selexpr_value *next;
-} t_selexpr_value;
-
-/*! \internal \brief
- * Describes a parsed method parameter.
- */
-typedef struct t_selexpr_param
-{
- /** Name of the parameter. */
- char *name;
- /** Number of values given for this parameter. */
- int nval;
- /** Pointer to the first value. */
- struct t_selexpr_value *value;
- /** Pointer to the next parameter. */
- struct t_selexpr_param *next;
-} t_selexpr_param;
-
-/** Error reporting function for the selection parser. */
-void
-_gmx_selparser_error(const char *fmt, ...);
-
-/** Allocates and initializes a constant \c t_selexpr_value. */
-t_selexpr_value *
-_gmx_selexpr_create_value(e_selvalue_t type);
-/** Allocates and initializes an expression \c t_selexpr_value. */
-t_selexpr_value *
-_gmx_selexpr_create_value_expr(struct t_selelem *expr);
-/** Allocates and initializes a \c t_selexpr_param. */
-t_selexpr_param *
-_gmx_selexpr_create_param(char *name);
-
-/** Frees the memory allocated for a chain of values. */
-void
-_gmx_selexpr_free_values(t_selexpr_value *value);
-/** Frees the memory allocated for a chain of parameters. */
-void
-_gmx_selexpr_free_params(t_selexpr_param *param);
-
-/** Propagates the flags for selection elements. */
-int
-_gmx_selelem_update_flags(struct t_selelem *sel);
-
-/** Initializes the method parameter data of \ref SEL_EXPRESSION and
- * \ref SEL_MODIFIER elements. */
-void
-_gmx_selelem_init_method_params(struct t_selelem *sel, void *scanner);
-/** Initializes the method for a \ref SEL_EXPRESSION selection element. */
-void
-_gmx_selelem_set_method(struct t_selelem *sel,
- struct gmx_ana_selmethod_t *method, void *scanner);
-
-/** Creates a \c t_selelem for arithmetic expression evaluation. */
-struct t_selelem *
-_gmx_sel_init_arithmetic(struct t_selelem *left, struct t_selelem *right,
- char op, void *scanner);
-/** Creates a \c t_selelem for comparsion expression evaluation. */
-struct t_selelem *
-_gmx_sel_init_comparison(struct t_selelem *left, struct t_selelem *right,
- char *cmpop, void *scanner);
-/** Creates a \c t_selelem for a keyword expression from the parsed data. */
-struct t_selelem *
-_gmx_sel_init_keyword(struct gmx_ana_selmethod_t *method,
- t_selexpr_value *args, const char *rpost, void *scanner);
-/** Creates a \c t_selelem for a method expression from the parsed data. */
-struct t_selelem *
-_gmx_sel_init_method(struct gmx_ana_selmethod_t *method,
- t_selexpr_param *params, const char *rpost,
- void *scanner);
-/** Creates a \c t_selelem for a modifier expression from the parsed data. */
-struct t_selelem *
-_gmx_sel_init_modifier(struct gmx_ana_selmethod_t *mod, t_selexpr_param *params,
- struct t_selelem *sel, void *scanner);
-/** Creates a \c t_selelem for evaluation of reference positions. */
-struct t_selelem *
-_gmx_sel_init_position(struct t_selelem *expr, const char *type, void *scanner);
-
-/** Creates a \c t_selelem for a constant position. */
-struct t_selelem *
-_gmx_sel_init_const_position(real x, real y, real z);
-/** Creates a \c t_selelem for a index group expression using group name. */
-struct t_selelem *
-_gmx_sel_init_group_by_name(const char *name, void *scanner);
-/** Creates a \c t_selelem for a index group expression using group index. */
-struct t_selelem *
-_gmx_sel_init_group_by_id(int id, void *scanner);
-/** Creates a \c t_selelem for a variable reference */
-struct t_selelem *
-_gmx_sel_init_variable_ref(struct t_selelem *sel);
-
-/** Creates a root \c t_selelem for a selection. */
-struct t_selelem *
-_gmx_sel_init_selection(char *name, struct t_selelem *sel, void *scanner);
-/** Creates a root \c t_selelem elements for a variable assignment. */
-struct t_selelem *
-_gmx_sel_assign_variable(char *name, struct t_selelem *expr, void *scanner);
-/** Appends a root \c t_selelem to a selection collection. */
-struct t_selelem *
-_gmx_sel_append_selection(struct t_selelem *sel, struct t_selelem *last,
- void *scanner);
-/** Check whether the parser should finish. */
-gmx_bool
-_gmx_sel_parser_should_finish(void *scanner);
-
-/** Handle empty commands. */
-void
-_gmx_sel_handle_empty_cmd(void *scanner);
-/** Process help commands. */
-void
-_gmx_sel_handle_help_cmd(char *topic, void *scanner);
-
-/* In params.c */
-/** Initializes an array of parameters based on input from the selection parser. */
-gmx_bool
-_gmx_sel_parse_params(t_selexpr_param *pparams, int nparam,
- struct gmx_ana_selparam_t *param, struct t_selelem *root,
- void *scanner);
-
-#endif
+++ /dev/null
-#!/bin/bash
-#
-# This script runs Bison and/or Flex to regenerate the files as follows:
-# parser.y -> parser.c, parser.h
-# scanner.l -> scanner.c, scanner_flex.h
-# The commands are run only if the generated files are older than the
-# Bison/Flex input files, or if a '-f' flag is provided.
-
-FORCE=
-if [ "x$1" == "x-f" ] ; then
- FORCE=1
-fi
-
-# For convenience, change to the directory where the files are located
-# if the script is run from the root of the source tree.
-dirname=src/gmxlib/selection
-if [[ -f $dirname/parser.y && -f $dirname/scanner.l ]] ; then
- cd $dirname
-fi
-
-[[ $FORCE || parser.y -nt parser.c ]] && bison -d -t -o parser.c parser.y
-[[ $FORCE || scanner.l -nt scanner.c ]] && flex -o scanner.c scanner.l
+++ /dev/null
-#line 2 "scanner.c"
-
-#line 4 "scanner.c"
-
-#define YY_INT_ALIGNED short int
-
-/* A lexical scanner generated by flex */
-
-#define FLEX_SCANNER
-#define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 35
-#if YY_FLEX_SUBMINOR_VERSION > 0
-#define FLEX_BETA
-#endif
-
-/* First, we deal with platform-specific or compiler-specific issues. */
-
-/* begin standard C headers. */
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-/* end standard C headers. */
-
-/* flex integer type definitions */
-
-#ifndef FLEXINT_H
-#define FLEXINT_H
-
-/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
-
-#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-
-/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
- * if you want the limit (max/min) macros for int types.
- */
-#ifndef __STDC_LIMIT_MACROS
-#define __STDC_LIMIT_MACROS 1
-#endif
-
-#include <inttypes.h>
-typedef int8_t flex_int8_t;
-typedef uint8_t flex_uint8_t;
-typedef int16_t flex_int16_t;
-typedef uint16_t flex_uint16_t;
-typedef int32_t flex_int32_t;
-typedef uint32_t flex_uint32_t;
-#else
-typedef signed char flex_int8_t;
-typedef short int flex_int16_t;
-typedef int flex_int32_t;
-typedef unsigned char flex_uint8_t;
-typedef unsigned short int flex_uint16_t;
-typedef unsigned int flex_uint32_t;
-
-/* Limits of integral types. */
-#ifndef INT8_MIN
-#define INT8_MIN (-128)
-#endif
-#ifndef INT16_MIN
-#define INT16_MIN (-32767-1)
-#endif
-#ifndef INT32_MIN
-#define INT32_MIN (-2147483647-1)
-#endif
-#ifndef INT8_MAX
-#define INT8_MAX (127)
-#endif
-#ifndef INT16_MAX
-#define INT16_MAX (32767)
-#endif
-#ifndef INT32_MAX
-#define INT32_MAX (2147483647)
-#endif
-#ifndef UINT8_MAX
-#define UINT8_MAX (255U)
-#endif
-#ifndef UINT16_MAX
-#define UINT16_MAX (65535U)
-#endif
-#ifndef UINT32_MAX
-#define UINT32_MAX (4294967295U)
-#endif
-
-#endif /* ! C99 */
-
-#endif /* ! FLEXINT_H */
-
-#ifdef __cplusplus
-
-/* The "const" storage-class-modifier is valid. */
-#define YY_USE_CONST
-
-#else /* ! __cplusplus */
-
-/* C99 requires __STDC__ to be defined as 1. */
-#if defined (__STDC__)
-
-#define YY_USE_CONST
-
-#endif /* defined (__STDC__) */
-#endif /* ! __cplusplus */
-
-#ifdef YY_USE_CONST
-#define yyconst const
-#else
-#define yyconst
-#endif
-
-/* Returned upon end-of-file. */
-#define YY_NULL 0
-
-/* Promotes a possibly negative, possibly signed char to an unsigned
- * integer for use as an array index. If the signed char is negative,
- * we want to instead treat it as an 8-bit unsigned char, hence the
- * double cast.
- */
-#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
-
-/* An opaque pointer. */
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#define YY_TYPEDEF_YY_SCANNER_T
-typedef void* yyscan_t;
-#endif
-
-/* For convenience, these vars (plus the bison vars far below)
- are macros in the reentrant scanner. */
-#define yyin yyg->yyin_r
-#define yyout yyg->yyout_r
-#define yyextra yyg->yyextra_r
-#define yyleng yyg->yyleng_r
-#define yytext yyg->yytext_r
-#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
-#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
-#define yy_flex_debug yyg->yy_flex_debug_r
-
-/* Enter a start condition. This macro really ought to take a parameter,
- * but we do it the disgusting crufty way forced on us by the ()-less
- * definition of BEGIN.
- */
-#define BEGIN yyg->yy_start = 1 + 2 *
-
-/* Translate the current start state into a value that can be later handed
- * to BEGIN to return to the state. The YYSTATE alias is for lex
- * compatibility.
- */
-#define YY_START ((yyg->yy_start - 1) / 2)
-#define YYSTATE YY_START
-
-/* Action number for EOF rule of a given start state. */
-#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
-
-/* Special action meaning "start processing a new file". */
-#define YY_NEW_FILE _gmx_sel_yyrestart(yyin ,yyscanner )
-
-#define YY_END_OF_BUFFER_CHAR 0
-
-/* Size of default input buffer. */
-#ifndef YY_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k.
- * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
- * Ditto for the __ia64__ case accordingly.
- */
-#define YY_BUF_SIZE 32768
-#else
-#define YY_BUF_SIZE 16384
-#endif /* __ia64__ */
-#endif
-
-/* The state buf must be large enough to hold one state per character in the main buffer.
- */
-#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
-
-#ifndef YY_TYPEDEF_YY_BUFFER_STATE
-#define YY_TYPEDEF_YY_BUFFER_STATE
-typedef struct yy_buffer_state *YY_BUFFER_STATE;
-#endif
-
-#define EOB_ACT_CONTINUE_SCAN 0
-#define EOB_ACT_END_OF_FILE 1
-#define EOB_ACT_LAST_MATCH 2
-
- #define YY_LESS_LINENO(n)
-
-/* Return all but the first "n" matched characters back to the input stream. */
-#define yyless(n) \
- do \
- { \
- /* Undo effects of setting up yytext. */ \
- int yyless_macro_arg = (n); \
- YY_LESS_LINENO(yyless_macro_arg);\
- *yy_cp = yyg->yy_hold_char; \
- YY_RESTORE_YY_MORE_OFFSET \
- yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
- YY_DO_BEFORE_ACTION; /* set up yytext again */ \
- } \
- while ( 0 )
-
-#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
-
-#ifndef YY_TYPEDEF_YY_SIZE_T
-#define YY_TYPEDEF_YY_SIZE_T
-typedef size_t yy_size_t;
-#endif
-
-#ifndef YY_STRUCT_YY_BUFFER_STATE
-#define YY_STRUCT_YY_BUFFER_STATE
-struct yy_buffer_state
- {
- FILE *yy_input_file;
-
- char *yy_ch_buf; /* input buffer */
- char *yy_buf_pos; /* current position in input buffer */
-
- /* Size of input buffer in bytes, not including room for EOB
- * characters.
- */
- yy_size_t yy_buf_size;
-
- /* Number of characters read into yy_ch_buf, not including EOB
- * characters.
- */
- int yy_n_chars;
-
- /* Whether we "own" the buffer - i.e., we know we created it,
- * and can realloc() it to grow it, and should free() it to
- * delete it.
- */
- int yy_is_our_buffer;
-
- /* Whether this is an "interactive" input source; if so, and
- * if we're using stdio for input, then we want to use getc()
- * instead of fread(), to make sure we stop fetching input after
- * each newline.
- */
- int yy_is_interactive;
-
- /* Whether we're considered to be at the beginning of a line.
- * If so, '^' rules will be active on the next match, otherwise
- * not.
- */
- int yy_at_bol;
-
- int yy_bs_lineno; /**< The line count. */
- int yy_bs_column; /**< The column count. */
-
- /* Whether to try to fill the input buffer when we reach the
- * end of it.
- */
- int yy_fill_buffer;
-
- int yy_buffer_status;
-
-#define YY_BUFFER_NEW 0
-#define YY_BUFFER_NORMAL 1
- /* When an EOF's been seen but there's still some text to process
- * then we mark the buffer as YY_EOF_PENDING, to indicate that we
- * shouldn't try reading from the input source any more. We might
- * still have a bunch of tokens to match, though, because of
- * possible backing-up.
- *
- * When we actually see the EOF, we change the status to "new"
- * (via _gmx_sel_yyrestart()), so that the user can continue scanning by
- * just pointing yyin at a new input file.
- */
-#define YY_BUFFER_EOF_PENDING 2
-
- };
-#endif /* !YY_STRUCT_YY_BUFFER_STATE */
-
-/* We provide macros for accessing buffer states in case in the
- * future we want to put the buffer states in a more general
- * "scanner state".
- *
- * Returns the top of the stack, or NULL.
- */
-#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
- ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
- : NULL)
-
-/* Same as previous macro, but useful when we know that the buffer stack is not
- * NULL or when we need an lvalue. For internal use only.
- */
-#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
-
-void _gmx_sel_yyrestart (FILE *input_file ,yyscan_t yyscanner );
-void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
-YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
-void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
-void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
-void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
-void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner );
-
-static void _gmx_sel_yyensure_buffer_stack (yyscan_t yyscanner );
-static void _gmx_sel_yy_load_buffer_state (yyscan_t yyscanner );
-static void _gmx_sel_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
-
-#define YY_FLUSH_BUFFER _gmx_sel_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
-
-YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
-YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
-YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
-
-void *_gmx_sel_yyalloc (yy_size_t ,yyscan_t yyscanner );
-void *_gmx_sel_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
-void _gmx_sel_yyfree (void * ,yyscan_t yyscanner );
-
-#define yy_new_buffer _gmx_sel_yy_create_buffer
-
-#define yy_set_interactive(is_interactive) \
- { \
- if ( ! YY_CURRENT_BUFFER ){ \
- _gmx_sel_yyensure_buffer_stack (yyscanner); \
- YY_CURRENT_BUFFER_LVALUE = \
- _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
- } \
- YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
- }
-
-#define yy_set_bol(at_bol) \
- { \
- if ( ! YY_CURRENT_BUFFER ){\
- _gmx_sel_yyensure_buffer_stack (yyscanner); \
- YY_CURRENT_BUFFER_LVALUE = \
- _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
- } \
- YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
- }
-
-#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
-
-#define _gmx_sel_yywrap(n) 1
-#define YY_SKIP_YYWRAP
-
-typedef unsigned char YY_CHAR;
-
-typedef int yy_state_type;
-
-#define yytext_ptr yytext_r
-
-static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
-static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
-static int yy_get_next_buffer (yyscan_t yyscanner );
-static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
-
-/* Done after the current pattern has been matched and before the
- * corresponding action - sets up yytext.
- */
-#define YY_DO_BEFORE_ACTION \
- yyg->yytext_ptr = yy_bp; \
- yyleng = (size_t) (yy_cp - yy_bp); \
- yyg->yy_hold_char = *yy_cp; \
- *yy_cp = '\0'; \
- yyg->yy_c_buf_p = yy_cp;
-
-#define YY_NUM_RULES 26
-#define YY_END_OF_BUFFER 27
-/* This struct is not used in this scanner,
- but its presence is necessary. */
-struct yy_trans_info
- {
- flex_int32_t yy_verify;
- flex_int32_t yy_nxt;
- };
-static yyconst flex_int16_t yy_accept[89] =
- { 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 27, 25, 23, 6, 20, 25, 1, 25, 25, 2,
- 6, 21, 25, 22, 25, 24, 22, 22, 22, 22,
- 22, 22, 25, 22, 22, 22, 22, 22, 11, 8,
- 10, 10, 9, 23, 21, 0, 4, 0, 1, 17,
- 3, 3, 2, 24, 24, 22, 5, 22, 22, 22,
- 18, 15, 22, 18, 16, 13, 22, 12, 22, 22,
- 8, 9, 0, 0, 3, 17, 22, 20, 19, 13,
- 22, 0, 3, 3, 22, 7, 14, 0
- } ;
-
-static yyconst flex_int32_t yy_ec[256] =
- { 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 2, 4, 5, 6, 1, 1, 7, 1, 1,
- 1, 1, 8, 1, 8, 9, 1, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 10, 1, 11, 12,
- 13, 12, 1, 1, 14, 14, 14, 14, 15, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 1, 16, 1, 1, 17, 1, 18, 14, 14, 19,
-
- 20, 21, 22, 23, 14, 14, 14, 24, 14, 25,
- 26, 27, 14, 28, 29, 30, 31, 14, 14, 32,
- 33, 14, 1, 34, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1
- } ;
-
-static yyconst flex_int32_t yy_meta[35] =
- { 0,
- 1, 1, 2, 1, 1, 1, 1, 1, 3, 4,
- 1, 1, 1, 4, 4, 1, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 1
- } ;
-
-static yyconst flex_int16_t yy_base[94] =
- { 0,
- 0, 0, 131, 130, 10, 12, 132, 131, 45, 0,
- 153, 158, 150, 158, 138, 75, 0, 143, 139, 72,
- 158, 135, 134, 0, 143, 136, 119, 115, 116, 113,
- 114, 113, 104, 62, 111, 68, 116, 115, 158, 132,
- 158, 158, 0, 131, 158, 79, 158, 127, 0, 158,
- 84, 87, 91, 122, 31, 0, 158, 111, 103, 98,
- 0, 0, 99, 158, 0, 96, 104, 0, 95, 99,
- 120, 0, 34, 107, 76, 0, 82, 0, 0, 0,
- 83, 99, 98, 95, 76, 0, 0, 158, 111, 115,
- 117, 94, 84
-
- } ;
-
-static yyconst flex_int16_t yy_def[94] =
- { 0,
- 88, 1, 1, 1, 1, 1, 1, 1, 88, 9,
- 88, 88, 88, 88, 88, 89, 90, 88, 88, 91,
- 88, 88, 88, 92, 88, 91, 92, 92, 92, 92,
- 92, 92, 88, 92, 92, 92, 92, 92, 88, 88,
- 88, 88, 93, 88, 88, 89, 88, 88, 90, 88,
- 88, 88, 91, 91, 91, 92, 88, 92, 92, 92,
- 92, 92, 92, 88, 92, 92, 92, 92, 92, 92,
- 88, 93, 88, 88, 91, 92, 92, 92, 92, 92,
- 92, 88, 88, 88, 92, 92, 92, 0, 88, 88,
- 88, 88, 88
-
- } ;
-
-static yyconst flex_int16_t yy_nxt[193] =
- { 0,
- 12, 13, 14, 15, 16, 17, 18, 12, 19, 20,
- 21, 22, 23, 24, 24, 25, 26, 27, 24, 24,
- 24, 28, 24, 24, 29, 30, 24, 24, 24, 31,
- 24, 32, 24, 33, 35, 36, 35, 36, 74, 88,
- 75, 82, 37, 83, 37, 39, 40, 41, 39, 39,
- 39, 39, 39, 39, 39, 42, 39, 39, 43, 43,
- 39, 39, 43, 43, 43, 43, 43, 43, 43, 43,
- 43, 43, 43, 43, 43, 43, 43, 43, 39, 47,
- 52, 53, 65, 47, 88, 75, 55, 72, 67, 61,
- 48, 55, 68, 51, 48, 61, 51, 56, 73, 52,
-
- 53, 73, 87, 73, 84, 55, 73, 83, 83, 86,
- 55, 46, 85, 46, 46, 49, 84, 49, 49, 54,
- 54, 71, 81, 68, 80, 78, 79, 78, 77, 76,
- 88, 46, 44, 71, 70, 69, 66, 64, 63, 62,
- 61, 60, 59, 58, 88, 57, 45, 45, 51, 50,
- 45, 44, 88, 38, 38, 34, 34, 11, 88, 88,
- 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
- 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
- 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
- 88, 88
-
- } ;
-
-static yyconst flex_int16_t yy_chk[193] =
- { 0,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 5, 5, 6, 6, 55, 55,
- 55, 73, 5, 73, 6, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 16,
- 20, 20, 34, 46, 75, 75, 20, 93, 36, 34,
- 16, 20, 36, 51, 46, 36, 52, 92, 51, 53,
-
- 53, 52, 85, 51, 84, 53, 52, 83, 82, 81,
- 53, 89, 77, 89, 89, 90, 74, 90, 90, 91,
- 91, 71, 70, 69, 67, 66, 63, 60, 59, 58,
- 54, 48, 44, 40, 38, 37, 35, 33, 32, 31,
- 30, 29, 28, 27, 26, 25, 23, 22, 19, 18,
- 15, 13, 11, 8, 7, 4, 3, 88, 88, 88,
- 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
- 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
- 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
- 88, 88
-
- } ;
-
-/* The intent behind this definition is that it'll catch
- * any uses of REJECT which flex missed.
- */
-#define REJECT reject_used_but_not_detected
-#define yymore() yymore_used_but_not_detected
-#define YY_MORE_ADJ 0
-#define YY_RESTORE_YY_MORE_OFFSET
-#line 1 "scanner.l"
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \cond \internal \file scanner.l
- * \brief
- * Tokenizer for the selection language.
- * \endcond
- */
-/*! \internal \file scanner.c
- * \brief
- * Generated (from scanner.l by Flex) tokenizer for the selection language.
- */
-#line 41 "scanner.l"
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string2.h>
-
-#include "parser.h"
-#include "scanner.h"
-#include "scanner_internal.h"
-
-/* This macro is here to make the actions a bit shorter, since nearly every
- * action needs this call. */
-#define ADD_TOKEN _gmx_sel_lexer_add_token(yytext, yyleng, state)
-
-#define YY_NO_UNISTD_H 1
-
-
-
-
-#line 571 "scanner.c"
-
-#define INITIAL 0
-#define matchof 1
-#define matchbool 2
-#define cmdstart 3
-#define help 4
-
-#ifndef YY_NO_UNISTD_H
-/* Special case for "unistd.h", since it is non-ANSI. We include it way
- * down here because we want the user's section 1 to have been scanned first.
- * The user has a chance to override it with an option.
- */
-#include <unistd.h>
-#endif
-
-#ifndef YY_EXTRA_TYPE
-#define YY_EXTRA_TYPE void *
-#endif
-
-/* Holds the entire state of the reentrant scanner. */
-struct yyguts_t
- {
-
- /* User-defined. Not touched by flex. */
- YY_EXTRA_TYPE yyextra_r;
-
- /* The rest are the same as the globals declared in the non-reentrant scanner. */
- FILE *yyin_r, *yyout_r;
- size_t yy_buffer_stack_top; /**< index of top of stack. */
- size_t yy_buffer_stack_max; /**< capacity of stack. */
- YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
- char yy_hold_char;
- int yy_n_chars;
- int yyleng_r;
- char *yy_c_buf_p;
- int yy_init;
- int yy_start;
- int yy_did_buffer_switch_on_eof;
- int yy_start_stack_ptr;
- int yy_start_stack_depth;
- int *yy_start_stack;
- yy_state_type yy_last_accepting_state;
- char* yy_last_accepting_cpos;
-
- int yylineno_r;
- int yy_flex_debug_r;
-
- char *yytext_r;
- int yy_more_flag;
- int yy_more_len;
-
- }; /* end struct yyguts_t */
-
-static int yy_init_globals (yyscan_t yyscanner );
-
-int _gmx_sel_yylex_init (yyscan_t* scanner);
-
-int _gmx_sel_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
-
-/* Accessor methods to globals.
- These are made visible to non-reentrant scanners for convenience. */
-
-int _gmx_sel_yylex_destroy (yyscan_t yyscanner );
-
-int _gmx_sel_yyget_debug (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_debug (int debug_flag ,yyscan_t yyscanner );
-
-YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
-
-FILE *_gmx_sel_yyget_in (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_in (FILE * in_str ,yyscan_t yyscanner );
-
-FILE *_gmx_sel_yyget_out (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_out (FILE * out_str ,yyscan_t yyscanner );
-
-int _gmx_sel_yyget_leng (yyscan_t yyscanner );
-
-char *_gmx_sel_yyget_text (yyscan_t yyscanner );
-
-int _gmx_sel_yyget_lineno (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_lineno (int line_number ,yyscan_t yyscanner );
-
-/* Macros after this point can all be overridden by user definitions in
- * section 1.
- */
-
-#ifndef YY_SKIP_YYWRAP
-#ifdef __cplusplus
-extern "C" int _gmx_sel_yywrap (yyscan_t yyscanner );
-#else
-extern int _gmx_sel_yywrap (yyscan_t yyscanner );
-#endif
-#endif
-
- static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
-
-#ifndef yytext_ptr
-static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
-#endif
-
-#ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
-#endif
-
-#ifndef YY_NO_INPUT
-
-#ifdef __cplusplus
-static int yyinput (yyscan_t yyscanner );
-#else
-static int input (yyscan_t yyscanner );
-#endif
-
-#endif
-
-/* Amount of stuff to slurp up with each read. */
-#ifndef YY_READ_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k */
-#define YY_READ_BUF_SIZE 16384
-#else
-#define YY_READ_BUF_SIZE 8192
-#endif /* __ia64__ */
-#endif
-
-/* Copy whatever the last rule matched to the standard output. */
-#ifndef ECHO
-/* This used to be an fputs(), but since the string might contain NUL's,
- * we now use fwrite().
- */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
-#endif
-
-/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
- * is returned in "result".
- */
-#ifndef YY_INPUT
-#define YY_INPUT(buf,result,max_size) \
- if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
- { \
- int c = '*'; \
- size_t n; \
- for ( n = 0; n < max_size && \
- (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
- buf[n] = (char) c; \
- if ( c == '\n' ) \
- buf[n++] = (char) c; \
- if ( c == EOF && ferror( yyin ) ) \
- YY_FATAL_ERROR( "input in flex scanner failed" ); \
- result = n; \
- } \
- else \
- { \
- errno=0; \
- while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
- { \
- if( errno != EINTR) \
- { \
- YY_FATAL_ERROR( "input in flex scanner failed" ); \
- break; \
- } \
- errno=0; \
- clearerr(yyin); \
- } \
- }\
-\
-
-#endif
-
-/* No semi-colon after return; correct usage is to write "yyterminate();" -
- * we don't want an extra ';' after the "return" because that will cause
- * some compilers to complain about unreachable statements.
- */
-#ifndef yyterminate
-#define yyterminate() return YY_NULL
-#endif
-
-/* Number of entries by which start-condition stack grows. */
-#ifndef YY_START_STACK_INCR
-#define YY_START_STACK_INCR 25
-#endif
-
-/* Report a fatal error. */
-#ifndef YY_FATAL_ERROR
-#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
-#endif
-
-/* end tables serialization structures and prototypes */
-
-/* Default declaration of generated scanner - a define so the user can
- * easily add parameters.
- */
-#ifndef YY_DECL
-#define YY_DECL_IS_OURS 1
-
-extern int _gmx_sel_yylex (yyscan_t yyscanner);
-
-#define YY_DECL int _gmx_sel_yylex (yyscan_t yyscanner)
-#endif /* !YY_DECL */
-
-/* Code executed at the beginning of each rule, after yytext and yyleng
- * have been set up.
- */
-#ifndef YY_USER_ACTION
-#define YY_USER_ACTION
-#endif
-
-/* Code executed at the end of each rule. */
-#ifndef YY_BREAK
-#define YY_BREAK break;
-#endif
-
-#define YY_RULE_SETUP \
- YY_USER_ACTION
-
-/** The main scanner function which does all the work.
- */
-YY_DECL
-{
- register yy_state_type yy_current_state;
- register char *yy_cp, *yy_bp;
- register int yy_act;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
-#line 80 "scanner.l"
-
-
-
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(yyscanner);
- int retval;
- /* Return a token if one is pending */
- retval = _gmx_sel_lexer_process_pending(yylval, state);
- if (retval != 0)
- {
- return retval;
- }
- /* Handle the start conditions for 'of' matching */
- if (state->bMatchOf)
- {
- BEGIN(matchof);
- state->bMatchOf = FALSE;
- }
- else if (state->bMatchBool)
- {
- BEGIN(matchbool);
- state->bMatchBool = FALSE;
- }
- else if (state->bCmdStart)
- {
- BEGIN(cmdstart);
- }
- else if (YYSTATE != help)
- {
- BEGIN(0);
- }
-
-
-#line 834 "scanner.c"
-
- if ( !yyg->yy_init )
- {
- yyg->yy_init = 1;
-
-#ifdef YY_USER_INIT
- YY_USER_INIT;
-#endif
-
- if ( ! yyg->yy_start )
- yyg->yy_start = 1; /* first start state */
-
- if ( ! yyin )
- yyin = stdin;
-
- if ( ! yyout )
- yyout = stdout;
-
- if ( ! YY_CURRENT_BUFFER ) {
- _gmx_sel_yyensure_buffer_stack (yyscanner);
- YY_CURRENT_BUFFER_LVALUE =
- _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
- }
-
- _gmx_sel_yy_load_buffer_state(yyscanner );
- }
-
- while ( 1 ) /* loops until end-of-file is reached */
- {
- yy_cp = yyg->yy_c_buf_p;
-
- /* Support of yytext. */
- *yy_cp = yyg->yy_hold_char;
-
- /* yy_bp points to the position in yy_ch_buf of the start of
- * the current run.
- */
- yy_bp = yy_cp;
-
- yy_current_state = yyg->yy_start;
-yy_match:
- do
- {
- register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
- if ( yy_accept[yy_current_state] )
- {
- yyg->yy_last_accepting_state = yy_current_state;
- yyg->yy_last_accepting_cpos = yy_cp;
- }
- while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
- {
- yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 89 )
- yy_c = yy_meta[(unsigned int) yy_c];
- }
- yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
- ++yy_cp;
- }
- while ( yy_current_state != 88 );
- yy_cp = yyg->yy_last_accepting_cpos;
- yy_current_state = yyg->yy_last_accepting_state;
-
-yy_find_action:
- yy_act = yy_accept[yy_current_state];
-
- YY_DO_BEFORE_ACTION;
-
-do_action: /* This label is used only to access EOF actions. */
-
- switch ( yy_act )
- { /* beginning of action switch */
- case 0: /* must back up */
- /* undo the effects of YY_DO_BEFORE_ACTION */
- *yy_cp = yyg->yy_hold_char;
- yy_cp = yyg->yy_last_accepting_cpos;
- yy_current_state = yyg->yy_last_accepting_state;
- goto yy_find_action;
-
-case 1:
-YY_RULE_SETUP
-#line 112 "scanner.l"
-
- YY_BREAK
-case 2:
-YY_RULE_SETUP
-#line 113 "scanner.l"
-{ yylval->i = strtol(yytext, NULL, 10); ADD_TOKEN; return TOK_INT; }
- YY_BREAK
-case 3:
-YY_RULE_SETUP
-#line 114 "scanner.l"
-{ yylval->r = strtod(yytext, NULL); ADD_TOKEN; return TOK_REAL; }
- YY_BREAK
-case 4:
-YY_RULE_SETUP
-#line 115 "scanner.l"
-{ yylval->str = gmx_strndup(yytext+1, yyleng-2); ADD_TOKEN; return STR; }
- YY_BREAK
-case 5:
-/* rule 5 can match eol */
-YY_RULE_SETUP
-#line 117 "scanner.l"
-{ _gmx_sel_lexer_add_token(" ", 1, state); }
- YY_BREAK
-case 6:
-/* rule 6 can match eol */
-YY_RULE_SETUP
-#line 118 "scanner.l"
-{
- if (yytext[0] == ';' || state->bInteractive)
- {
- rtrim(state->pselstr);
- return CMD_SEP;
- }
- else
- {
- _gmx_sel_lexer_add_token(" ", 1, state);
- }
- }
- YY_BREAK
-case 7:
-YY_RULE_SETUP
-#line 130 "scanner.l"
-{ BEGIN(help); return HELP; }
- YY_BREAK
-
-case 8:
-YY_RULE_SETUP
-#line 132 "scanner.l"
-
- YY_BREAK
-case 9:
-YY_RULE_SETUP
-#line 133 "scanner.l"
-{ yylval->str = gmx_strndup(yytext, yyleng); return HELP_TOPIC; }
- YY_BREAK
-case 10:
-/* rule 10 can match eol */
-YY_RULE_SETUP
-#line 134 "scanner.l"
-{ return CMD_SEP; }
- YY_BREAK
-case 11:
-YY_RULE_SETUP
-#line 135 "scanner.l"
-{ return INVALID; }
- YY_BREAK
-
-
-case 12:
-YY_RULE_SETUP
-#line 139 "scanner.l"
-{ ADD_TOKEN; yylval->i = 1; return TOK_INT; }
- YY_BREAK
-case 13:
-YY_RULE_SETUP
-#line 140 "scanner.l"
-{ ADD_TOKEN; yylval->i = 0; return TOK_INT; }
- YY_BREAK
-
-case 14:
-YY_RULE_SETUP
-#line 142 "scanner.l"
-{ ADD_TOKEN; return GROUP; }
- YY_BREAK
-case 15:
-YY_RULE_SETUP
-#line 143 "scanner.l"
-{ ADD_TOKEN; return TO; }
- YY_BREAK
-case 16:
-YY_RULE_SETUP
-#line 144 "scanner.l"
-{ ADD_TOKEN; BEGIN(0); return OF; }
- YY_BREAK
-case 17:
-YY_RULE_SETUP
-#line 145 "scanner.l"
-{ ADD_TOKEN; return AND; }
- YY_BREAK
-case 18:
-YY_RULE_SETUP
-#line 146 "scanner.l"
-{ ADD_TOKEN; return OR; }
- YY_BREAK
-case 19:
-YY_RULE_SETUP
-#line 147 "scanner.l"
-{ ADD_TOKEN; return XOR; }
- YY_BREAK
-case 20:
-YY_RULE_SETUP
-#line 148 "scanner.l"
-{ ADD_TOKEN; return NOT; }
- YY_BREAK
-case 21:
-YY_RULE_SETUP
-#line 149 "scanner.l"
-{ yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return CMP_OP; }
- YY_BREAK
-case 22:
-YY_RULE_SETUP
-#line 151 "scanner.l"
-{ return _gmx_sel_lexer_process_identifier(yylval, yytext, yyleng, state); }
- YY_BREAK
-case 23:
-YY_RULE_SETUP
-#line 153 "scanner.l"
-{ _gmx_sel_lexer_add_token(" ", 1, state); }
- YY_BREAK
-case 24:
-YY_RULE_SETUP
-#line 154 "scanner.l"
-{ yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return STR; }
- YY_BREAK
-case 25:
-YY_RULE_SETUP
-#line 155 "scanner.l"
-{ ADD_TOKEN; return yytext[0]; }
- YY_BREAK
-case 26:
-YY_RULE_SETUP
-#line 156 "scanner.l"
-YY_FATAL_ERROR( "flex scanner jammed" );
- YY_BREAK
-#line 1060 "scanner.c"
-case YY_STATE_EOF(INITIAL):
-case YY_STATE_EOF(matchof):
-case YY_STATE_EOF(matchbool):
-case YY_STATE_EOF(cmdstart):
-case YY_STATE_EOF(help):
- yyterminate();
-
- case YY_END_OF_BUFFER:
- {
- /* Amount of text matched not including the EOB char. */
- int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
-
- /* Undo the effects of YY_DO_BEFORE_ACTION. */
- *yy_cp = yyg->yy_hold_char;
- YY_RESTORE_YY_MORE_OFFSET
-
- if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
- {
- /* We're scanning a new file or input source. It's
- * possible that this happened because the user
- * just pointed yyin at a new source and called
- * _gmx_sel_yylex(). If so, then we have to assure
- * consistency between YY_CURRENT_BUFFER and our
- * globals. Here is the right place to do so, because
- * this is the first action (other than possibly a
- * back-up) that will match for the new input source.
- */
- yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
- YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
- YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
- }
-
- /* Note that here we test for yy_c_buf_p "<=" to the position
- * of the first EOB in the buffer, since yy_c_buf_p will
- * already have been incremented past the NUL character
- * (since all states make transitions on EOB to the
- * end-of-buffer state). Contrast this with the test
- * in input().
- */
- if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
- { /* This was really a NUL. */
- yy_state_type yy_next_state;
-
- yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
-
- yy_current_state = yy_get_previous_state( yyscanner );
-
- /* Okay, we're now positioned to make the NUL
- * transition. We couldn't have
- * yy_get_previous_state() go ahead and do it
- * for us because it doesn't know how to deal
- * with the possibility of jamming (and we don't
- * want to build jamming into it because then it
- * will run more slowly).
- */
-
- yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
-
- yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
-
- if ( yy_next_state )
- {
- /* Consume the NUL. */
- yy_cp = ++yyg->yy_c_buf_p;
- yy_current_state = yy_next_state;
- goto yy_match;
- }
-
- else
- {
- yy_cp = yyg->yy_last_accepting_cpos;
- yy_current_state = yyg->yy_last_accepting_state;
- goto yy_find_action;
- }
- }
-
- else switch ( yy_get_next_buffer( yyscanner ) )
- {
- case EOB_ACT_END_OF_FILE:
- {
- yyg->yy_did_buffer_switch_on_eof = 0;
-
- if ( _gmx_sel_yywrap(yyscanner ) )
- {
- /* Note: because we've taken care in
- * yy_get_next_buffer() to have set up
- * yytext, we can now set up
- * yy_c_buf_p so that if some total
- * hoser (like flex itself) wants to
- * call the scanner after we return the
- * YY_NULL, it'll still work - another
- * YY_NULL will get returned.
- */
- yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
-
- yy_act = YY_STATE_EOF(YY_START);
- goto do_action;
- }
-
- else
- {
- if ( ! yyg->yy_did_buffer_switch_on_eof )
- YY_NEW_FILE;
- }
- break;
- }
-
- case EOB_ACT_CONTINUE_SCAN:
- yyg->yy_c_buf_p =
- yyg->yytext_ptr + yy_amount_of_matched_text;
-
- yy_current_state = yy_get_previous_state( yyscanner );
-
- yy_cp = yyg->yy_c_buf_p;
- yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
- goto yy_match;
-
- case EOB_ACT_LAST_MATCH:
- yyg->yy_c_buf_p =
- &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
-
- yy_current_state = yy_get_previous_state( yyscanner );
-
- yy_cp = yyg->yy_c_buf_p;
- yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
- goto yy_find_action;
- }
- break;
- }
-
- default:
- YY_FATAL_ERROR(
- "fatal flex scanner internal error--no action found" );
- } /* end of action switch */
- } /* end of scanning one token */
-} /* end of _gmx_sel_yylex */
-
-/* yy_get_next_buffer - try to read in a new buffer
- *
- * Returns a code representing an action:
- * EOB_ACT_LAST_MATCH -
- * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
- * EOB_ACT_END_OF_FILE - end of file
- */
-static int yy_get_next_buffer (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
- register char *source = yyg->yytext_ptr;
- register int number_to_move, i;
- int ret_val;
-
- if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
- YY_FATAL_ERROR(
- "fatal flex scanner internal error--end of buffer missed" );
-
- if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
- { /* Don't try to fill the buffer, so this is an EOF. */
- if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
- {
- /* We matched a single character, the EOB, so
- * treat this as a final EOF.
- */
- return EOB_ACT_END_OF_FILE;
- }
-
- else
- {
- /* We matched some text prior to the EOB, first
- * process it.
- */
- return EOB_ACT_LAST_MATCH;
- }
- }
-
- /* Try to read more data. */
-
- /* First move last chars to start of buffer. */
- number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
-
- for ( i = 0; i < number_to_move; ++i )
- *(dest++) = *(source++);
-
- if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
- /* don't do the read, it's not guaranteed to return an EOF,
- * just force an EOF
- */
- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
-
- else
- {
- int num_to_read =
- YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
-
- while ( num_to_read <= 0 )
- { /* Not enough room in the buffer - grow it. */
-
- /* just a shorter name for the current buffer */
- YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
-
- int yy_c_buf_p_offset =
- (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
-
- if ( b->yy_is_our_buffer )
- {
- int new_size = b->yy_buf_size * 2;
-
- if ( new_size <= 0 )
- b->yy_buf_size += b->yy_buf_size / 8;
- else
- b->yy_buf_size *= 2;
-
- b->yy_ch_buf = (char *)
- /* Include room in for 2 EOB chars. */
- _gmx_sel_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
- }
- else
- /* Can't grow it, we don't own it. */
- b->yy_ch_buf = 0;
-
- if ( ! b->yy_ch_buf )
- YY_FATAL_ERROR(
- "fatal error - scanner input buffer overflow" );
-
- yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
-
- num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
- number_to_move - 1;
-
- }
-
- if ( num_to_read > YY_READ_BUF_SIZE )
- num_to_read = YY_READ_BUF_SIZE;
-
- /* Read in more data. */
- YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
- yyg->yy_n_chars, (size_t) num_to_read );
-
- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
- }
-
- if ( yyg->yy_n_chars == 0 )
- {
- if ( number_to_move == YY_MORE_ADJ )
- {
- ret_val = EOB_ACT_END_OF_FILE;
- _gmx_sel_yyrestart(yyin ,yyscanner);
- }
-
- else
- {
- ret_val = EOB_ACT_LAST_MATCH;
- YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
- YY_BUFFER_EOF_PENDING;
- }
- }
-
- else
- ret_val = EOB_ACT_CONTINUE_SCAN;
-
- if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
- /* Extend the array by 50%, plus the number we really need. */
- yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
- YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) _gmx_sel_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
- if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
- YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
- }
-
- yyg->yy_n_chars += number_to_move;
- YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
- YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
-
- yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
-
- return ret_val;
-}
-
-/* yy_get_previous_state - get the state just before the EOB char was reached */
-
- static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
-{
- register yy_state_type yy_current_state;
- register char *yy_cp;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- yy_current_state = yyg->yy_start;
-
- for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
- {
- register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
- if ( yy_accept[yy_current_state] )
- {
- yyg->yy_last_accepting_state = yy_current_state;
- yyg->yy_last_accepting_cpos = yy_cp;
- }
- while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
- {
- yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 89 )
- yy_c = yy_meta[(unsigned int) yy_c];
- }
- yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
- }
-
- return yy_current_state;
-}
-
-/* yy_try_NUL_trans - try to make a transition on the NUL character
- *
- * synopsis
- * next_state = yy_try_NUL_trans( current_state );
- */
- static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
-{
- register int yy_is_jam;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
- register char *yy_cp = yyg->yy_c_buf_p;
-
- register YY_CHAR yy_c = 1;
- if ( yy_accept[yy_current_state] )
- {
- yyg->yy_last_accepting_state = yy_current_state;
- yyg->yy_last_accepting_cpos = yy_cp;
- }
- while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
- {
- yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 89 )
- yy_c = yy_meta[(unsigned int) yy_c];
- }
- yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
- yy_is_jam = (yy_current_state == 88);
-
- return yy_is_jam ? 0 : yy_current_state;
-}
-
- static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
-{
- register char *yy_cp;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- yy_cp = yyg->yy_c_buf_p;
-
- /* undo effects of setting up yytext */
- *yy_cp = yyg->yy_hold_char;
-
- if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
- { /* need to shift things up to make room */
- /* +2 for EOB chars. */
- register int number_to_move = yyg->yy_n_chars + 2;
- register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
- YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
- register char *source =
- &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
-
- while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
- *--dest = *--source;
-
- yy_cp += (int) (dest - source);
- yy_bp += (int) (dest - source);
- YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
- yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
-
- if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
- YY_FATAL_ERROR( "flex scanner push-back overflow" );
- }
-
- *--yy_cp = (char) c;
-
- yyg->yytext_ptr = yy_bp;
- yyg->yy_hold_char = *yy_cp;
- yyg->yy_c_buf_p = yy_cp;
-}
-
-#ifndef YY_NO_INPUT
-#ifdef __cplusplus
- static int yyinput (yyscan_t yyscanner)
-#else
- static int input (yyscan_t yyscanner)
-#endif
-
-{
- int c;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- *yyg->yy_c_buf_p = yyg->yy_hold_char;
-
- if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
- {
- /* yy_c_buf_p now points to the character we want to return.
- * If this occurs *before* the EOB characters, then it's a
- * valid NUL; if not, then we've hit the end of the buffer.
- */
- if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
- /* This was really a NUL. */
- *yyg->yy_c_buf_p = '\0';
-
- else
- { /* need more input */
- int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
- ++yyg->yy_c_buf_p;
-
- switch ( yy_get_next_buffer( yyscanner ) )
- {
- case EOB_ACT_LAST_MATCH:
- /* This happens because yy_g_n_b()
- * sees that we've accumulated a
- * token and flags that we need to
- * try matching the token before
- * proceeding. But for input(),
- * there's no matching to consider.
- * So convert the EOB_ACT_LAST_MATCH
- * to EOB_ACT_END_OF_FILE.
- */
-
- /* Reset buffer status. */
- _gmx_sel_yyrestart(yyin ,yyscanner);
-
- /*FALLTHROUGH*/
-
- case EOB_ACT_END_OF_FILE:
- {
- if ( _gmx_sel_yywrap(yyscanner ) )
- return EOF;
-
- if ( ! yyg->yy_did_buffer_switch_on_eof )
- YY_NEW_FILE;
-#ifdef __cplusplus
- return yyinput(yyscanner);
-#else
- return input(yyscanner);
-#endif
- }
-
- case EOB_ACT_CONTINUE_SCAN:
- yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
- break;
- }
- }
- }
-
- c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
- *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
- yyg->yy_hold_char = *++yyg->yy_c_buf_p;
-
- return c;
-}
-#endif /* ifndef YY_NO_INPUT */
-
-/** Immediately switch to a different input stream.
- * @param input_file A readable stream.
- * @param yyscanner The scanner object.
- * @note This function does not reset the start condition to @c INITIAL .
- */
- void _gmx_sel_yyrestart (FILE * input_file , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- if ( ! YY_CURRENT_BUFFER ){
- _gmx_sel_yyensure_buffer_stack (yyscanner);
- YY_CURRENT_BUFFER_LVALUE =
- _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
- }
-
- _gmx_sel_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
- _gmx_sel_yy_load_buffer_state(yyscanner );
-}
-
-/** Switch to a different input buffer.
- * @param new_buffer The new input buffer.
- * @param yyscanner The scanner object.
- */
- void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- /* TODO. We should be able to replace this entire function body
- * with
- * _gmx_sel_yypop_buffer_state();
- * _gmx_sel_yypush_buffer_state(new_buffer);
- */
- _gmx_sel_yyensure_buffer_stack (yyscanner);
- if ( YY_CURRENT_BUFFER == new_buffer )
- return;
-
- if ( YY_CURRENT_BUFFER )
- {
- /* Flush out information for old buffer. */
- *yyg->yy_c_buf_p = yyg->yy_hold_char;
- YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
- }
-
- YY_CURRENT_BUFFER_LVALUE = new_buffer;
- _gmx_sel_yy_load_buffer_state(yyscanner );
-
- /* We don't actually know whether we did this switch during
- * EOF (_gmx_sel_yywrap()) processing, but the only time this flag
- * is looked at is after _gmx_sel_yywrap() is called, so it's safe
- * to go ahead and always set it.
- */
- yyg->yy_did_buffer_switch_on_eof = 1;
-}
-
-static void _gmx_sel_yy_load_buffer_state (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
- yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
- yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
- yyg->yy_hold_char = *yyg->yy_c_buf_p;
-}
-
-/** Allocate and initialize an input buffer state.
- * @param file A readable stream.
- * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
- * @param yyscanner The scanner object.
- * @return the allocated buffer state.
- */
- YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
-{
- YY_BUFFER_STATE b;
-
- b = (YY_BUFFER_STATE) _gmx_sel_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
- if ( ! b )
- YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_create_buffer()" );
-
- b->yy_buf_size = size;
-
- /* yy_ch_buf has to be 2 characters longer than the size given because
- * we need to put in 2 end-of-buffer characters.
- */
- b->yy_ch_buf = (char *) _gmx_sel_yyalloc(b->yy_buf_size + 2 ,yyscanner );
- if ( ! b->yy_ch_buf )
- YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_create_buffer()" );
-
- b->yy_is_our_buffer = 1;
-
- _gmx_sel_yy_init_buffer(b,file ,yyscanner);
-
- return b;
-}
-
-/** Destroy the buffer.
- * @param b a buffer created with _gmx_sel_yy_create_buffer()
- * @param yyscanner The scanner object.
- */
- void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- if ( ! b )
- return;
-
- if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
- YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
-
- if ( b->yy_is_our_buffer )
- _gmx_sel_yyfree((void *) b->yy_ch_buf ,yyscanner );
-
- _gmx_sel_yyfree((void *) b ,yyscanner );
-}
-
-/* Initializes or reinitializes a buffer.
- * This function is sometimes called more than once on the same buffer,
- * such as during a _gmx_sel_yyrestart() or at EOF.
- */
- static void _gmx_sel_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
-
-{
- int oerrno = errno;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- _gmx_sel_yy_flush_buffer(b ,yyscanner);
-
- b->yy_input_file = file;
- b->yy_fill_buffer = 1;
-
- /* If b is the current buffer, then _gmx_sel_yy_init_buffer was _probably_
- * called from _gmx_sel_yyrestart() or through yy_get_next_buffer.
- * In that case, we don't want to reset the lineno or column.
- */
- if (b != YY_CURRENT_BUFFER){
- b->yy_bs_lineno = 1;
- b->yy_bs_column = 0;
- }
-
- b->yy_is_interactive = 0;
-
- errno = oerrno;
-}
-
-/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
- * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
- * @param yyscanner The scanner object.
- */
- void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- if ( ! b )
- return;
-
- b->yy_n_chars = 0;
-
- /* We always need two end-of-buffer characters. The first causes
- * a transition to the end-of-buffer state. The second causes
- * a jam in that state.
- */
- b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
- b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
-
- b->yy_buf_pos = &b->yy_ch_buf[0];
-
- b->yy_at_bol = 1;
- b->yy_buffer_status = YY_BUFFER_NEW;
-
- if ( b == YY_CURRENT_BUFFER )
- _gmx_sel_yy_load_buffer_state(yyscanner );
-}
-
-/** Pushes the new state onto the stack. The new state becomes
- * the current state. This function will allocate the stack
- * if necessary.
- * @param new_buffer The new state.
- * @param yyscanner The scanner object.
- */
-void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- if (new_buffer == NULL)
- return;
-
- _gmx_sel_yyensure_buffer_stack(yyscanner);
-
- /* This block is copied from _gmx_sel_yy_switch_to_buffer. */
- if ( YY_CURRENT_BUFFER )
- {
- /* Flush out information for old buffer. */
- *yyg->yy_c_buf_p = yyg->yy_hold_char;
- YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
- }
-
- /* Only push if top exists. Otherwise, replace top. */
- if (YY_CURRENT_BUFFER)
- yyg->yy_buffer_stack_top++;
- YY_CURRENT_BUFFER_LVALUE = new_buffer;
-
- /* copied from _gmx_sel_yy_switch_to_buffer. */
- _gmx_sel_yy_load_buffer_state(yyscanner );
- yyg->yy_did_buffer_switch_on_eof = 1;
-}
-
-/** Removes and deletes the top of the stack, if present.
- * The next element becomes the new top.
- * @param yyscanner The scanner object.
- */
-void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- if (!YY_CURRENT_BUFFER)
- return;
-
- _gmx_sel_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
- YY_CURRENT_BUFFER_LVALUE = NULL;
- if (yyg->yy_buffer_stack_top > 0)
- --yyg->yy_buffer_stack_top;
-
- if (YY_CURRENT_BUFFER) {
- _gmx_sel_yy_load_buffer_state(yyscanner );
- yyg->yy_did_buffer_switch_on_eof = 1;
- }
-}
-
-/* Allocates the stack if it does not exist.
- * Guarantees space for at least one push.
- */
-static void _gmx_sel_yyensure_buffer_stack (yyscan_t yyscanner)
-{
- int num_to_alloc;
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- if (!yyg->yy_buffer_stack) {
-
- /* First allocation is just for 2 elements, since we don't know if this
- * scanner will even need a stack. We use 2 instead of 1 to avoid an
- * immediate realloc on the next call.
- */
- num_to_alloc = 1;
- yyg->yy_buffer_stack = (struct yy_buffer_state**)_gmx_sel_yyalloc
- (num_to_alloc * sizeof(struct yy_buffer_state*)
- , yyscanner);
- if ( ! yyg->yy_buffer_stack )
- YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yyensure_buffer_stack()" );
-
- memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
-
- yyg->yy_buffer_stack_max = num_to_alloc;
- yyg->yy_buffer_stack_top = 0;
- return;
- }
-
- if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
-
- /* Increase the buffer to prepare for a possible push. */
- int grow_size = 8 /* arbitrary grow size */;
-
- num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
- yyg->yy_buffer_stack = (struct yy_buffer_state**)_gmx_sel_yyrealloc
- (yyg->yy_buffer_stack,
- num_to_alloc * sizeof(struct yy_buffer_state*)
- , yyscanner);
- if ( ! yyg->yy_buffer_stack )
- YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yyensure_buffer_stack()" );
-
- /* zero only the new slots.*/
- memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
- yyg->yy_buffer_stack_max = num_to_alloc;
- }
-}
-
-/** Setup the input buffer state to scan directly from a user-specified character buffer.
- * @param base the character buffer
- * @param size the size in bytes of the character buffer
- * @param yyscanner The scanner object.
- * @return the newly allocated buffer state object.
- */
-YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
-{
- YY_BUFFER_STATE b;
-
- if ( size < 2 ||
- base[size-2] != YY_END_OF_BUFFER_CHAR ||
- base[size-1] != YY_END_OF_BUFFER_CHAR )
- /* They forgot to leave room for the EOB's. */
- return 0;
-
- b = (YY_BUFFER_STATE) _gmx_sel_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
- if ( ! b )
- YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_scan_buffer()" );
-
- b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
- b->yy_buf_pos = b->yy_ch_buf = base;
- b->yy_is_our_buffer = 0;
- b->yy_input_file = 0;
- b->yy_n_chars = b->yy_buf_size;
- b->yy_is_interactive = 0;
- b->yy_at_bol = 1;
- b->yy_fill_buffer = 0;
- b->yy_buffer_status = YY_BUFFER_NEW;
-
- _gmx_sel_yy_switch_to_buffer(b ,yyscanner );
-
- return b;
-}
-
-/** Setup the input buffer state to scan a string. The next call to _gmx_sel_yylex() will
- * scan from a @e copy of @a str.
- * @param yystr a NUL-terminated string to scan
- * @param yyscanner The scanner object.
- * @return the newly allocated buffer state object.
- * @note If you want to scan bytes that may contain NUL values, then use
- * _gmx_sel_yy_scan_bytes() instead.
- */
-YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
-{
-
- return _gmx_sel_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
-}
-
-/** Setup the input buffer state to scan the given bytes. The next call to _gmx_sel_yylex() will
- * scan from a @e copy of @a bytes.
- * @param yybytes the byte buffer to scan
- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
- * @param yyscanner The scanner object.
- * @return the newly allocated buffer state object.
- */
-YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
-{
- YY_BUFFER_STATE b;
- char *buf;
- yy_size_t n;
- int i;
-
- /* Get memory for full buffer, including space for trailing EOB's. */
- n = _yybytes_len + 2;
- buf = (char *) _gmx_sel_yyalloc(n ,yyscanner );
- if ( ! buf )
- YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_scan_bytes()" );
-
- for ( i = 0; i < _yybytes_len; ++i )
- buf[i] = yybytes[i];
-
- buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
-
- b = _gmx_sel_yy_scan_buffer(buf,n ,yyscanner);
- if ( ! b )
- YY_FATAL_ERROR( "bad buffer in _gmx_sel_yy_scan_bytes()" );
-
- /* It's okay to grow etc. this buffer, and we should throw it
- * away when we're done.
- */
- b->yy_is_our_buffer = 1;
-
- return b;
-}
-
-#ifndef YY_EXIT_FAILURE
-#define YY_EXIT_FAILURE 2
-#endif
-
-static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
-{
- (void) fprintf( stderr, "%s\n", msg );
- exit( YY_EXIT_FAILURE );
-}
-
-/* Redefine yyless() so it works in section 3 code. */
-
-#undef yyless
-#define yyless(n) \
- do \
- { \
- /* Undo effects of setting up yytext. */ \
- int yyless_macro_arg = (n); \
- YY_LESS_LINENO(yyless_macro_arg);\
- yytext[yyleng] = yyg->yy_hold_char; \
- yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
- yyg->yy_hold_char = *yyg->yy_c_buf_p; \
- *yyg->yy_c_buf_p = '\0'; \
- yyleng = yyless_macro_arg; \
- } \
- while ( 0 )
-
-/* Accessor methods (get/set functions) to struct members. */
-
-/** Get the user-defined data for this scanner.
- * @param yyscanner The scanner object.
- */
-YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- return yyextra;
-}
-
-/** Get the current line number.
- * @param yyscanner The scanner object.
- */
-int _gmx_sel_yyget_lineno (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- if (! YY_CURRENT_BUFFER)
- return 0;
-
- return yylineno;
-}
-
-/** Get the current column number.
- * @param yyscanner The scanner object.
- */
-int _gmx_sel_yyget_column (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- if (! YY_CURRENT_BUFFER)
- return 0;
-
- return yycolumn;
-}
-
-/** Get the input stream.
- * @param yyscanner The scanner object.
- */
-FILE *_gmx_sel_yyget_in (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- return yyin;
-}
-
-/** Get the output stream.
- * @param yyscanner The scanner object.
- */
-FILE *_gmx_sel_yyget_out (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- return yyout;
-}
-
-/** Get the length of the current token.
- * @param yyscanner The scanner object.
- */
-int _gmx_sel_yyget_leng (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- return yyleng;
-}
-
-/** Get the current token.
- * @param yyscanner The scanner object.
- */
-
-char *_gmx_sel_yyget_text (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- return yytext;
-}
-
-/** Set the user-defined data. This data is never touched by the scanner.
- * @param user_defined The data to be associated with this scanner.
- * @param yyscanner The scanner object.
- */
-void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- yyextra = user_defined ;
-}
-
-/** Set the current line number.
- * @param line_number
- * @param yyscanner The scanner object.
- */
-void _gmx_sel_yyset_lineno (int line_number , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- /* lineno is only valid if an input buffer exists. */
- if (! YY_CURRENT_BUFFER )
- yy_fatal_error( "_gmx_sel_yyset_lineno called with no buffer" , yyscanner);
-
- yylineno = line_number;
-}
-
-/** Set the current column.
- * @param line_number
- * @param yyscanner The scanner object.
- */
-void _gmx_sel_yyset_column (int column_no , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- /* column is only valid if an input buffer exists. */
- if (! YY_CURRENT_BUFFER )
- yy_fatal_error( "_gmx_sel_yyset_column called with no buffer" , yyscanner);
-
- yycolumn = column_no;
-}
-
-/** Set the input stream. This does not discard the current
- * input buffer.
- * @param in_str A readable stream.
- * @param yyscanner The scanner object.
- * @see _gmx_sel_yy_switch_to_buffer
- */
-void _gmx_sel_yyset_in (FILE * in_str , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- yyin = in_str ;
-}
-
-void _gmx_sel_yyset_out (FILE * out_str , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- yyout = out_str ;
-}
-
-int _gmx_sel_yyget_debug (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- return yy_flex_debug;
-}
-
-void _gmx_sel_yyset_debug (int bdebug , yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- yy_flex_debug = bdebug ;
-}
-
-/* Accessor methods for yylval and yylloc */
-
-/* User-visible API */
-
-/* _gmx_sel_yylex_init is special because it creates the scanner itself, so it is
- * the ONLY reentrant function that doesn't take the scanner as the last argument.
- * That's why we explicitly handle the declaration, instead of using our macros.
- */
-
-int _gmx_sel_yylex_init(yyscan_t* ptr_yy_globals)
-
-{
- if (ptr_yy_globals == NULL){
- errno = EINVAL;
- return 1;
- }
-
- *ptr_yy_globals = (yyscan_t) _gmx_sel_yyalloc ( sizeof( struct yyguts_t ), NULL );
-
- if (*ptr_yy_globals == NULL){
- errno = ENOMEM;
- return 1;
- }
-
- /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
- memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
-
- return yy_init_globals ( *ptr_yy_globals );
-}
-
-/* _gmx_sel_yylex_init_extra has the same functionality as _gmx_sel_yylex_init, but follows the
- * convention of taking the scanner as the last argument. Note however, that
- * this is a *pointer* to a scanner, as it will be allocated by this call (and
- * is the reason, too, why this function also must handle its own declaration).
- * The user defined value in the first argument will be available to _gmx_sel_yyalloc in
- * the yyextra field.
- */
-
-int _gmx_sel_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
-
-{
- struct yyguts_t dummy_yyguts;
-
- _gmx_sel_yyset_extra (yy_user_defined, &dummy_yyguts);
-
- if (ptr_yy_globals == NULL){
- errno = EINVAL;
- return 1;
- }
-
- *ptr_yy_globals = (yyscan_t) _gmx_sel_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
-
- if (*ptr_yy_globals == NULL){
- errno = ENOMEM;
- return 1;
- }
-
- /* By setting to 0xAA, we expose bugs in
- yy_init_globals. Leave at 0x00 for releases. */
- memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
-
- _gmx_sel_yyset_extra (yy_user_defined, *ptr_yy_globals);
-
- return yy_init_globals ( *ptr_yy_globals );
-}
-
-static int yy_init_globals (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
- /* Initialization is the same as for the non-reentrant scanner.
- * This function is called from _gmx_sel_yylex_destroy(), so don't allocate here.
- */
-
- yyg->yy_buffer_stack = 0;
- yyg->yy_buffer_stack_top = 0;
- yyg->yy_buffer_stack_max = 0;
- yyg->yy_c_buf_p = (char *) 0;
- yyg->yy_init = 0;
- yyg->yy_start = 0;
-
- yyg->yy_start_stack_ptr = 0;
- yyg->yy_start_stack_depth = 0;
- yyg->yy_start_stack = NULL;
-
-/* Defined in main.c */
-#ifdef YY_STDINIT
- yyin = stdin;
- yyout = stdout;
-#else
- yyin = (FILE *) 0;
- yyout = (FILE *) 0;
-#endif
-
- /* For future reference: Set errno on error, since we are called by
- * _gmx_sel_yylex_init()
- */
- return 0;
-}
-
-/* _gmx_sel_yylex_destroy is for both reentrant and non-reentrant scanners. */
-int _gmx_sel_yylex_destroy (yyscan_t yyscanner)
-{
- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-
- /* Pop the buffer stack, destroying each element. */
- while(YY_CURRENT_BUFFER){
- _gmx_sel_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
- YY_CURRENT_BUFFER_LVALUE = NULL;
- _gmx_sel_yypop_buffer_state(yyscanner);
- }
-
- /* Destroy the stack itself. */
- _gmx_sel_yyfree(yyg->yy_buffer_stack ,yyscanner);
- yyg->yy_buffer_stack = NULL;
-
- /* Destroy the start condition stack. */
- _gmx_sel_yyfree(yyg->yy_start_stack ,yyscanner );
- yyg->yy_start_stack = NULL;
-
- /* Reset the globals. This is important in a non-reentrant scanner so the next time
- * _gmx_sel_yylex() is called, initialization will occur. */
- yy_init_globals( yyscanner);
-
- /* Destroy the main struct (reentrant only). */
- _gmx_sel_yyfree ( yyscanner , yyscanner );
- yyscanner = NULL;
- return 0;
-}
-
-/*
- * Internal utility routines.
- */
-
-#ifndef yytext_ptr
-static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
-{
- register int i;
- for ( i = 0; i < n; ++i )
- s1[i] = s2[i];
-}
-#endif
-
-#ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
-{
- register int n;
- for ( n = 0; s[n]; ++n )
- ;
-
- return n;
-}
-#endif
-
-void *_gmx_sel_yyalloc (yy_size_t size , yyscan_t yyscanner)
-{
- return (void *) malloc( size );
-}
-
-void *_gmx_sel_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
-{
- /* The cast to (char *) in the following accommodates both
- * implementations that use char* generic pointers, and those
- * that use void* generic pointers. It works with the latter
- * because both ANSI C and C++ allow castless assignment from
- * any pointer type to void*, and deal with argument conversions
- * as though doing an assignment.
- */
- return (void *) realloc( (char *) ptr, size );
-}
-
-void _gmx_sel_yyfree (void * ptr , yyscan_t yyscanner)
-{
- free( (char *) ptr ); /* see _gmx_sel_yyrealloc() for (char *) cast */
-}
-
-#define YYTABLES_NAME "yytables"
-
-#line 156 "scanner.l"
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief
- * Parser/scanner interaction functions.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_SCANNER_H
-#define SELECTION_SCANNER_H
-
-#include "parser.h"
-
-struct gmx_ana_indexgrps_t;
-struct gmx_ana_selcollection_t;
-
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#define YY_TYPEDEF_YY_SCANNER_T
-typedef void *yyscan_t;
-#endif
-
-/** Initializes the selection scanner. */
-int
-_gmx_sel_init_lexer(yyscan_t *scannerp, struct gmx_ana_selcollection_t *sc,
- gmx_bool bInteractive, int maxnr,
- struct gmx_ana_indexgrps_t *grps);
-/** Frees memory allocated for the selection scanner. */
-void
-_gmx_sel_free_lexer(yyscan_t scanner);
-
-/** Returns TRUE if the scanner is interactive. */
-gmx_bool
-_gmx_sel_is_lexer_interactive(yyscan_t scanner);
-/** Returns the selection collection for the scanner. */
-struct gmx_ana_selcollection_t *
-_gmx_sel_lexer_selcollection(yyscan_t scanner);
-/** Returns the external index groups for the scanner. */
-struct gmx_ana_indexgrps_t *
-_gmx_sel_lexer_indexgrps(yyscan_t scanner);
-/** Returns the number of selections after which the parser should stop. */
-int
-_gmx_sel_lexer_exp_selcount(yyscan_t scanner);
-
-/** Returns a pretty string of the current selection. */
-const char *
-_gmx_sel_lexer_pselstr(yyscan_t scanner);
-/** Clears the current selection string. */
-void
-_gmx_sel_lexer_clear_pselstr(yyscan_t scanner);
-/** Clears the method stack in the scanner in error situations. */
-void
-_gmx_sel_lexer_clear_method_stack(yyscan_t scanner);
-/** Notifies the scanner that a complete method expression has been parsed. */
-void
-_gmx_sel_finish_method(yyscan_t scanner);
-/** Initializes the scanner to scan a file. */
-void
-_gmx_sel_set_lex_input_file(yyscan_t scanner, FILE *fp);
-/** Initializes the scanner to scan a string. */
-void
-_gmx_sel_set_lex_input_str(yyscan_t scanner, const char *str);
-
-/** A wrapper for the actual scanner, used by the Bison parser. */
-int
-_gmx_sel_yyblex(YYSTYPE *yylval, yyscan_t yyscanner);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \cond \internal \file scanner.l
- * \brief
- * Tokenizer for the selection language.
- * \endcond
- */
-/*! \internal \file scanner.c
- * \brief
- * Generated (from scanner.l by Flex) tokenizer for the selection language.
- */
-%{
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string2.h>
-
-#include "parser.h"
-#include "scanner.h"
-#include "scanner_internal.h"
-
-/* This macro is here to make the actions a bit shorter, since nearly every
- * action needs this call. */
-#define ADD_TOKEN _gmx_sel_lexer_add_token(yytext, yyleng, state)
-
-%}
-
-INTEGER [[:digit:]]+
-DSEQ ([[:digit:]]+)
-FRAC (([[:digit:]]*"."{DSEQ})|{DSEQ}".")
-EXP ([eE][+-]?{DSEQ})
-REAL (({FRAC}{EXP}?)|({DSEQ}{EXP}))
-STRING (\"([^\"\\\n]|(\\\"))*\")
-IDENTIFIER ([[:alpha:]][_[:alnum:]]*)
-CMPOP (([<>]=?)|([!=]=))
-COMMENT (#.*)
-
-%option nodefault
-%option noyywrap
-%option reentrant
-%option prefix="_gmx_sel_yy"
-%option header-file="scanner_flex.h"
-%option nounistd
-%option never-interactive
-
-%s matchof
-%s matchbool
-%s cmdstart
-%x help
-
-%%
-
-%{
- gmx_sel_lexer_t *state = yyget_extra(yyscanner);
- int retval;
- /* Return a token if one is pending */
- retval = _gmx_sel_lexer_process_pending(yylval, state);
- if (retval != 0)
- {
- return retval;
- }
- /* Handle the start conditions for 'of' matching */
- if (state->bMatchOf)
- {
- BEGIN(matchof);
- state->bMatchOf = FALSE;
- }
- else if (state->bMatchBool)
- {
- BEGIN(matchbool);
- state->bMatchBool = FALSE;
- }
- else if (state->bCmdStart)
- {
- BEGIN(cmdstart);
- }
- else if (YYSTATE != help)
- {
- BEGIN(0);
- }
-%}
-
-{COMMENT}
-{INTEGER} { yylval->i = strtol(yytext, NULL, 10); ADD_TOKEN; return TOK_INT; }
-{REAL} { yylval->r = strtod(yytext, NULL); ADD_TOKEN; return TOK_REAL; }
-{STRING} { yylval->str = gmx_strndup(yytext+1, yyleng-2); ADD_TOKEN; return STR; }
-
-\\\n { _gmx_sel_lexer_add_token(" ", 1, state); }
-";"|\n {
- if (yytext[0] == ';' || state->bInteractive)
- {
- rtrim(state->pselstr);
- return CMD_SEP;
- }
- else
- {
- _gmx_sel_lexer_add_token(" ", 1, state);
- }
- }
-
-<cmdstart>help { BEGIN(help); return HELP; }
-<help>{
-[[:blank:]]+
-{IDENTIFIER} { yylval->str = gmx_strndup(yytext, yyleng); return HELP_TOPIC; }
-";"|\n { return CMD_SEP; }
-. { return INVALID; }
-}
-
-<matchbool>{
-yes|on { ADD_TOKEN; yylval->i = 1; return TOK_INT; }
-no|off { ADD_TOKEN; yylval->i = 0; return TOK_INT; }
-}
-group { ADD_TOKEN; return GROUP; }
-to { ADD_TOKEN; return TO; }
-<matchof>of { ADD_TOKEN; BEGIN(0); return OF; }
-and|"&&" { ADD_TOKEN; return AND; }
-or|"||" { ADD_TOKEN; return OR; }
-xor { ADD_TOKEN; return XOR; }
-not|"!" { ADD_TOKEN; return NOT; }
-{CMPOP} { yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return CMP_OP; }
-
-{IDENTIFIER} { return _gmx_sel_lexer_process_identifier(yylval, yytext, yyleng, state); }
-
-[[:blank:]]+ { _gmx_sel_lexer_add_token(" ", 1, state); }
-[_[:alnum:]]+ { yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return STR; }
-. { ADD_TOKEN; return yytext[0]; }
+++ /dev/null
-#ifndef _gmx_sel_yyHEADER_H
-#define _gmx_sel_yyHEADER_H 1
-#define _gmx_sel_yyIN_HEADER 1
-
-#line 6 "scanner_flex.h"
-
-#line 8 "scanner_flex.h"
-
-#define YY_INT_ALIGNED short int
-
-/* A lexical scanner generated by flex */
-
-#define FLEX_SCANNER
-#define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 35
-#if YY_FLEX_SUBMINOR_VERSION > 0
-#define FLEX_BETA
-#endif
-
-/* First, we deal with platform-specific or compiler-specific issues. */
-
-/* begin standard C headers. */
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-/* end standard C headers. */
-
-/* flex integer type definitions */
-
-#ifndef FLEXINT_H
-#define FLEXINT_H
-
-/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
-
-#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-
-/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
- * if you want the limit (max/min) macros for int types.
- */
-#ifndef __STDC_LIMIT_MACROS
-#define __STDC_LIMIT_MACROS 1
-#endif
-
-#include <inttypes.h>
-typedef int8_t flex_int8_t;
-typedef uint8_t flex_uint8_t;
-typedef int16_t flex_int16_t;
-typedef uint16_t flex_uint16_t;
-typedef int32_t flex_int32_t;
-typedef uint32_t flex_uint32_t;
-#else
-typedef signed char flex_int8_t;
-typedef short int flex_int16_t;
-typedef int flex_int32_t;
-typedef unsigned char flex_uint8_t;
-typedef unsigned short int flex_uint16_t;
-typedef unsigned int flex_uint32_t;
-
-/* Limits of integral types. */
-#ifndef INT8_MIN
-#define INT8_MIN (-128)
-#endif
-#ifndef INT16_MIN
-#define INT16_MIN (-32767-1)
-#endif
-#ifndef INT32_MIN
-#define INT32_MIN (-2147483647-1)
-#endif
-#ifndef INT8_MAX
-#define INT8_MAX (127)
-#endif
-#ifndef INT16_MAX
-#define INT16_MAX (32767)
-#endif
-#ifndef INT32_MAX
-#define INT32_MAX (2147483647)
-#endif
-#ifndef UINT8_MAX
-#define UINT8_MAX (255U)
-#endif
-#ifndef UINT16_MAX
-#define UINT16_MAX (65535U)
-#endif
-#ifndef UINT32_MAX
-#define UINT32_MAX (4294967295U)
-#endif
-
-#endif /* ! C99 */
-
-#endif /* ! FLEXINT_H */
-
-#ifdef __cplusplus
-
-/* The "const" storage-class-modifier is valid. */
-#define YY_USE_CONST
-
-#else /* ! __cplusplus */
-
-/* C99 requires __STDC__ to be defined as 1. */
-#if defined (__STDC__)
-
-#define YY_USE_CONST
-
-#endif /* defined (__STDC__) */
-#endif /* ! __cplusplus */
-
-#ifdef YY_USE_CONST
-#define yyconst const
-#else
-#define yyconst
-#endif
-
-/* An opaque pointer. */
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#define YY_TYPEDEF_YY_SCANNER_T
-typedef void* yyscan_t;
-#endif
-
-/* For convenience, these vars (plus the bison vars far below)
- are macros in the reentrant scanner. */
-#define yyin yyg->yyin_r
-#define yyout yyg->yyout_r
-#define yyextra yyg->yyextra_r
-#define yyleng yyg->yyleng_r
-#define yytext yyg->yytext_r
-#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
-#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
-#define yy_flex_debug yyg->yy_flex_debug_r
-
-/* Size of default input buffer. */
-#ifndef YY_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k.
- * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
- * Ditto for the __ia64__ case accordingly.
- */
-#define YY_BUF_SIZE 32768
-#else
-#define YY_BUF_SIZE 16384
-#endif /* __ia64__ */
-#endif
-
-#ifndef YY_TYPEDEF_YY_BUFFER_STATE
-#define YY_TYPEDEF_YY_BUFFER_STATE
-typedef struct yy_buffer_state *YY_BUFFER_STATE;
-#endif
-
-#ifndef YY_TYPEDEF_YY_SIZE_T
-#define YY_TYPEDEF_YY_SIZE_T
-typedef size_t yy_size_t;
-#endif
-
-#ifndef YY_STRUCT_YY_BUFFER_STATE
-#define YY_STRUCT_YY_BUFFER_STATE
-struct yy_buffer_state
- {
- FILE *yy_input_file;
-
- char *yy_ch_buf; /* input buffer */
- char *yy_buf_pos; /* current position in input buffer */
-
- /* Size of input buffer in bytes, not including room for EOB
- * characters.
- */
- yy_size_t yy_buf_size;
-
- /* Number of characters read into yy_ch_buf, not including EOB
- * characters.
- */
- int yy_n_chars;
-
- /* Whether we "own" the buffer - i.e., we know we created it,
- * and can realloc() it to grow it, and should free() it to
- * delete it.
- */
- int yy_is_our_buffer;
-
- /* Whether this is an "interactive" input source; if so, and
- * if we're using stdio for input, then we want to use getc()
- * instead of fread(), to make sure we stop fetching input after
- * each newline.
- */
- int yy_is_interactive;
-
- /* Whether we're considered to be at the beginning of a line.
- * If so, '^' rules will be active on the next match, otherwise
- * not.
- */
- int yy_at_bol;
-
- int yy_bs_lineno; /**< The line count. */
- int yy_bs_column; /**< The column count. */
-
- /* Whether to try to fill the input buffer when we reach the
- * end of it.
- */
- int yy_fill_buffer;
-
- int yy_buffer_status;
-
- };
-#endif /* !YY_STRUCT_YY_BUFFER_STATE */
-
-void _gmx_sel_yyrestart (FILE *input_file ,yyscan_t yyscanner );
-void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
-YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
-void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
-void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
-void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
-void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner );
-
-YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
-YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
-YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
-
-void *_gmx_sel_yyalloc (yy_size_t ,yyscan_t yyscanner );
-void *_gmx_sel_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
-void _gmx_sel_yyfree (void * ,yyscan_t yyscanner );
-
-#define _gmx_sel_yywrap(n) 1
-#define YY_SKIP_YYWRAP
-
-#define yytext_ptr yytext_r
-
-#ifdef YY_HEADER_EXPORT_START_CONDITIONS
-#define INITIAL 0
-#define matchof 1
-#define matchbool 2
-#define cmdstart 3
-#define help 4
-
-#endif
-
-#ifndef YY_NO_UNISTD_H
-/* Special case for "unistd.h", since it is non-ANSI. We include it way
- * down here because we want the user's section 1 to have been scanned first.
- * The user has a chance to override it with an option.
- */
-#include <unistd.h>
-#endif
-
-#ifndef YY_EXTRA_TYPE
-#define YY_EXTRA_TYPE void *
-#endif
-
-int _gmx_sel_yylex_init (yyscan_t* scanner);
-
-int _gmx_sel_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
-
-/* Accessor methods to globals.
- These are made visible to non-reentrant scanners for convenience. */
-
-int _gmx_sel_yylex_destroy (yyscan_t yyscanner );
-
-int _gmx_sel_yyget_debug (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_debug (int debug_flag ,yyscan_t yyscanner );
-
-YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
-
-FILE *_gmx_sel_yyget_in (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_in (FILE * in_str ,yyscan_t yyscanner );
-
-FILE *_gmx_sel_yyget_out (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_out (FILE * out_str ,yyscan_t yyscanner );
-
-int _gmx_sel_yyget_leng (yyscan_t yyscanner );
-
-char *_gmx_sel_yyget_text (yyscan_t yyscanner );
-
-int _gmx_sel_yyget_lineno (yyscan_t yyscanner );
-
-void _gmx_sel_yyset_lineno (int line_number ,yyscan_t yyscanner );
-
-/* Macros after this point can all be overridden by user definitions in
- * section 1.
- */
-
-#ifndef YY_SKIP_YYWRAP
-#ifdef __cplusplus
-extern "C" int _gmx_sel_yywrap (yyscan_t yyscanner );
-#else
-extern int _gmx_sel_yywrap (yyscan_t yyscanner );
-#endif
-#endif
-
-#ifndef yytext_ptr
-static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
-#endif
-
-#ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
-#endif
-
-#ifndef YY_NO_INPUT
-
-#endif
-
-/* Amount of stuff to slurp up with each read. */
-#ifndef YY_READ_BUF_SIZE
-#ifdef __ia64__
-/* On IA-64, the buffer size is 16k, not 8k */
-#define YY_READ_BUF_SIZE 16384
-#else
-#define YY_READ_BUF_SIZE 8192
-#endif /* __ia64__ */
-#endif
-
-/* Number of entries by which start-condition stack grows. */
-#ifndef YY_START_STACK_INCR
-#define YY_START_STACK_INCR 25
-#endif
-
-/* Default declaration of generated scanner - a define so the user can
- * easily add parameters.
- */
-#ifndef YY_DECL
-#define YY_DECL_IS_OURS 1
-
-extern int _gmx_sel_yylex (yyscan_t yyscanner);
-
-#define YY_DECL int _gmx_sel_yylex (yyscan_t yyscanner)
-#endif /* !YY_DECL */
-
-/* yy_get_previous_state - get the state just before the EOB char was reached */
-
-#undef YY_NEW_FILE
-#undef YY_FLUSH_BUFFER
-#undef yy_set_bol
-#undef yy_new_buffer
-#undef yy_set_interactive
-#undef YY_DO_BEFORE_ACTION
-
-#ifdef YY_DECL_IS_OURS
-#undef YY_DECL_IS_OURS
-#undef YY_DECL
-#endif
-
-#line 156 "scanner.l"
-
-#line 349 "scanner_flex.h"
-#undef _gmx_sel_yyIN_HEADER
-#endif /* _gmx_sel_yyHEADER_H */
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Helper functions for the selection tokenizer.
- *
- * This file implements the functions in the headers scanner.h and
- * scanner_internal.h.
- */
-/*! \internal file scanner_flex.h
- * \brief Generated (from scanner.l) header file by Flex.
- *
- * This file contains definitions of functions that are needed in
- * scanner_internal.c.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <typedefs.h>
-#include <smalloc.h>
-#include <string.h>
-
-#include "string2.h"
-#include "gmx_fatal.h"
-
-#include <selmethod.h>
-
-#include "parsetree.h"
-#include "selcollection.h"
-#include "selelem.h"
-#include "symrec.h"
-
-#include "parser.h"
-#include "scanner.h"
-#include "scanner_internal.h"
-
-#define STRSTORE_ALLOCSTEP 1000
-
-/* These are defined as macros in the generated scanner_flex.h.
- * We undefine them here to have them as variable names in the subroutines.
- * There are other ways of doing this, but this is probably the easiest. */
-#undef yylval
-#undef yytext
-#undef yyleng
-
-static gmx_bool
-read_stdin_line(gmx_sel_lexer_t *state)
-{
- char *ptr = state->inputstr;
- int max_len = state->nalloc_input;
- int totlen = 0;
-
- if (feof(stdin))
- {
- return FALSE;
- }
- if (state->bInteractive)
- {
- fprintf(stderr, "> ");
- }
- /* For some reason (at least on my Linux), fgets() doesn't return until
- * the user presses Ctrl-D _twice_ at the end of a non-empty line.
- * This can be a bit confusing for users, but there's not much we can
- * do, and the chances of a normal user noticing this are not very big. */
- while (fgets(ptr, max_len, stdin))
- {
- int len = strlen(ptr);
-
- totlen += len;
- if (len >= 2 && ptr[len - 1] == '\n' && ptr[len - 2] == '\\')
- {
- if (state->bInteractive)
- {
- fprintf(stderr, "... ");
- }
- }
- else if (len >= 1 && ptr[len - 1] == '\n')
- {
- break;
- }
- else if (len < max_len - 1)
- {
- if (state->bInteractive)
- {
- fprintf(stderr, "\n");
- }
- break;
- }
- ptr += len;
- max_len -= len;
- if (max_len <= 2)
- {
- max_len += state->nalloc_input;
- state->nalloc_input *= 2;
- len = ptr - state->inputstr;
- srenew(state->inputstr, state->nalloc_input);
- ptr = state->inputstr + len;
- }
- }
- if (ferror(stdin))
- {
- gmx_input("selection reading failed");
- }
- return totlen > 0;
-}
-
-int
-_gmx_sel_yyblex(YYSTYPE *yylval, yyscan_t yyscanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(yyscanner);
- gmx_bool bCmdStart;
- int token;
-
- if (!state->bBuffer && !state->inputstr)
- {
- state->nalloc_input = 1024;
- snew(state->inputstr, state->nalloc_input);
- read_stdin_line(state);
- _gmx_sel_set_lex_input_str(yyscanner, state->inputstr);
- }
- bCmdStart = state->bCmdStart;
- token = _gmx_sel_yylex(yylval, yyscanner);
- while (state->inputstr && token == 0 && read_stdin_line(state))
- {
- _gmx_sel_set_lex_input_str(yyscanner, state->inputstr);
- token = _gmx_sel_yylex(yylval, yyscanner);
- }
- if (token == 0 && !bCmdStart)
- {
- token = CMD_SEP;
- rtrim(state->pselstr);
- }
- state->bCmdStart = (token == CMD_SEP);
- return token;
-}
-
-static int
-init_param_token(YYSTYPE *yylval, gmx_ana_selparam_t *param, gmx_bool bBoolNo)
-{
- if (bBoolNo)
- {
- snew(yylval->str, strlen(param->name) + 3);
- yylval->str[0] = 'n';
- yylval->str[1] = 'o';
- strcpy(yylval->str+2, param->name);
- }
- else
- {
- yylval->str = param->name ? strdup(param->name) : NULL;
- }
- return PARAM;
-}
-
-static int
-init_method_token(YYSTYPE *yylval, gmx_ana_selmethod_t *method, gmx_bool bPosMod,
- gmx_sel_lexer_t *state)
-{
- /* If the previous token was not KEYWORD_POS, return EMPTY_POSMOD
- * before the actual method to work around a limitation in Bison. */
- if (!bPosMod && method->type != POS_VALUE)
- {
- state->nextmethod = method;
- return EMPTY_POSMOD;
- }
- yylval->meth = method;
- if (!(method->flags & SMETH_MODIFIER) && method->nparams == 0)
- {
- /* Keyword */
- switch (method->type)
- {
- case INT_VALUE: return KEYWORD_NUMERIC;
- case REAL_VALUE: return KEYWORD_NUMERIC;
- case STR_VALUE: return KEYWORD_STR;
- case GROUP_VALUE: return KEYWORD_GROUP;
- default: return INVALID;
- }
- } else {
- /* Method with parameters or a modifier */
- if (method->flags & SMETH_MODIFIER)
- {
- /* Remove all methods from the stack */
- state->msp = -1;
- if (method->param[1].name == NULL)
- {
- state->nextparam = &method->param[1];
- }
- }
- else
- {
- if (method->param[0].name == NULL)
- {
- state->nextparam = &method->param[0];
- }
- }
- ++state->msp;
- if (state->msp >= state->mstack_alloc)
- {
- state->mstack_alloc += 10;
- srenew(state->mstack, state->mstack_alloc);
- }
- state->mstack[state->msp] = method;
- if (method->flags & SMETH_MODIFIER)
- {
- return MODIFIER;
- }
- switch (method->type)
- {
- case INT_VALUE: return METHOD_NUMERIC;
- case REAL_VALUE: return METHOD_NUMERIC;
- case POS_VALUE: return METHOD_POS;
- case GROUP_VALUE: return METHOD_GROUP;
- default:
- --state->msp;
- return INVALID;
- }
- }
- return INVALID; /* Should not be reached */
-}
-
-int
-_gmx_sel_lexer_process_pending(YYSTYPE *yylval, gmx_sel_lexer_t *state)
-{
- if (state->nextparam)
- {
- gmx_ana_selparam_t *param = state->nextparam;
- gmx_bool bBoolNo = state->bBoolNo;
-
- if (state->neom > 0)
- {
- --state->neom;
- return END_OF_METHOD;
- }
- state->nextparam = NULL;
- state->bBoolNo = FALSE;
- _gmx_sel_lexer_add_token(param->name, -1, state);
- return init_param_token(yylval, param, bBoolNo);
- }
- if (state->prev_pos_kw > 0)
- {
- --state->prev_pos_kw;
- }
- if (state->nextmethod)
- {
- gmx_ana_selmethod_t *method = state->nextmethod;
-
- state->nextmethod = NULL;
- return init_method_token(yylval, method, TRUE, state);
- }
- return 0;
-}
-
-int
-_gmx_sel_lexer_process_identifier(YYSTYPE *yylval, char *yytext, size_t yyleng,
- gmx_sel_lexer_t *state)
-{
- gmx_sel_symrec_t *symbol;
- e_symbol_t symtype;
-
- /* Check if the identifier matches with a parameter name */
- if (state->msp >= 0)
- {
- gmx_ana_selparam_t *param = NULL;
- gmx_bool bBoolNo = FALSE;
- int sp = state->msp;
- while (!param && sp >= 0)
- {
- int i;
- for (i = 0; i < state->mstack[sp]->nparams; ++i)
- {
- /* Skip NULL parameters and too long parameters */
- if (state->mstack[sp]->param[i].name == NULL
- || strlen(state->mstack[sp]->param[i].name) > yyleng)
- {
- continue;
- }
- if (!strncmp(state->mstack[sp]->param[i].name, yytext, yyleng))
- {
- param = &state->mstack[sp]->param[i];
- break;
- }
- /* Check separately for a 'no' prefix on gmx_boolean parameters */
- if (state->mstack[sp]->param[i].val.type == NO_VALUE
- && yyleng > 2 && yytext[0] == 'n' && yytext[1] == 'o'
- && !strncmp(state->mstack[sp]->param[i].name, yytext+2, yyleng-2))
- {
- param = &state->mstack[sp]->param[i];
- bBoolNo = TRUE;
- break;
- }
- }
- if (!param)
- {
- --sp;
- }
- }
- if (param)
- {
- if (param->val.type == NO_VALUE && !bBoolNo)
- {
- state->bMatchBool = TRUE;
- }
- if (sp < state->msp)
- {
- state->neom = state->msp - sp - 1;
- state->nextparam = param;
- state->bBoolNo = bBoolNo;
- return END_OF_METHOD;
- }
- _gmx_sel_lexer_add_token(param->name, -1, state);
- return init_param_token(yylval, param, bBoolNo);
- }
- }
-
- /* Check if the identifier matches with a symbol */
- symbol = _gmx_sel_find_symbol_len(state->sc->symtab, yytext, yyleng, FALSE);
- /* If there is no match, return the token as a string */
- if (!symbol)
- {
- yylval->str = gmx_strndup(yytext, yyleng);
- _gmx_sel_lexer_add_token(yytext, yyleng, state);
- return IDENTIFIER;
- }
- _gmx_sel_lexer_add_token(_gmx_sel_sym_name(symbol), -1, state);
- symtype = _gmx_sel_sym_type(symbol);
- /* Reserved symbols should have been caught earlier */
- if (symtype == SYMBOL_RESERVED)
- {
- return INVALID;
- }
- /* For variable symbols, return the type of the variable value */
- if (symtype == SYMBOL_VARIABLE)
- {
- t_selelem *var;
-
- var = _gmx_sel_sym_value_var(symbol);
- /* Return simple tokens for constant variables */
- if (var->type == SEL_CONST)
- {
- switch (var->v.type)
- {
- case INT_VALUE:
- yylval->i = var->v.u.i[0];
- return TOK_INT;
- case REAL_VALUE:
- yylval->r = var->v.u.r[0];
- return TOK_REAL;
- case POS_VALUE:
- break;
- default:
- return INVALID;
- }
- }
- yylval->sel = var;
- switch (var->v.type)
- {
- case INT_VALUE: return VARIABLE_NUMERIC;
- case REAL_VALUE: return VARIABLE_NUMERIC;
- case POS_VALUE: return VARIABLE_POS;
- case GROUP_VALUE: return VARIABLE_GROUP;
- default: return INVALID;
- }
- return INVALID;
- }
- /* For method symbols, return the correct type */
- if (symtype == SYMBOL_METHOD)
- {
- gmx_ana_selmethod_t *method;
-
- method = _gmx_sel_sym_value_method(symbol);
- return init_method_token(yylval, method, state->prev_pos_kw > 0, state);
- }
- /* For position symbols, we need to return KEYWORD_POS, but we also need
- * some additional handling. */
- if (symtype == SYMBOL_POS)
- {
- state->bMatchOf = TRUE;
- yylval->str = _gmx_sel_sym_name(symbol);
- state->prev_pos_kw = 2;
- return KEYWORD_POS;
- }
- /* Should not be reached */
- return INVALID;
-}
-
-void
-_gmx_sel_lexer_add_token(const char *str, int len, gmx_sel_lexer_t *state)
-{
- /* Do nothing if the string is empty, or if it is a space and there is
- * no other text yet, or if there already is a space. */
- if (!str || len == 0 || strlen(str) == 0
- || (str[0] == ' ' && str[1] == 0
- && (state->pslen == 0 || state->pselstr[state->pslen - 1] == ' ')))
- {
- return;
- }
- if (len < 0)
- {
- len = strlen(str);
- }
- /* Allocate more memory if necessary */
- if (state->nalloc_psel - state->pslen < len)
- {
- int incr = STRSTORE_ALLOCSTEP < len ? len : STRSTORE_ALLOCSTEP;
- state->nalloc_psel += incr;
- srenew(state->pselstr, state->nalloc_psel);
- }
- /* Append the token to the stored string */
- strncpy(state->pselstr + state->pslen, str, len);
- state->pslen += len;
- state->pselstr[state->pslen] = 0;
-}
-
-int
-_gmx_sel_init_lexer(yyscan_t *scannerp, struct gmx_ana_selcollection_t *sc,
- gmx_bool bInteractive, int maxnr,
- struct gmx_ana_indexgrps_t *grps)
-{
- gmx_sel_lexer_t *state;
- int rc;
-
- rc = _gmx_sel_yylex_init(scannerp);
- if (rc != 0)
- {
- return rc;
- }
-
- snew(state, 1);
- state->sc = sc;
- state->grps = grps;
- state->nexpsel = (maxnr > 0 ? sc->nr + maxnr : -1);
-
- state->bInteractive = bInteractive;
- state->nalloc_input = 0;
- state->inputstr = NULL;
-
- snew(state->pselstr, STRSTORE_ALLOCSTEP);
- state->pselstr[0] = 0;
- state->pslen = 0;
- state->nalloc_psel = STRSTORE_ALLOCSTEP;
-
- snew(state->mstack, 20);
- state->mstack_alloc = 20;
- state->msp = -1;
- state->neom = 0;
- state->nextparam = NULL;
- state->nextmethod = NULL;
- state->prev_pos_kw = 0;
- state->bBoolNo = FALSE;
- state->bMatchOf = FALSE;
- state->bMatchBool = FALSE;
- state->bCmdStart = TRUE;
- state->bBuffer = FALSE;
-
- _gmx_sel_yyset_extra(state, *scannerp);
- return 0;
-}
-
-void
-_gmx_sel_free_lexer(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
-
- sfree(state->inputstr);
- sfree(state->pselstr);
- sfree(state->mstack);
- if (state->bBuffer)
- {
- _gmx_sel_yy_delete_buffer(state->buffer, scanner);
- }
- sfree(state);
- _gmx_sel_yylex_destroy(scanner);
-}
-
-gmx_bool
-_gmx_sel_is_lexer_interactive(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
- return state->bInteractive;
-}
-
-struct gmx_ana_selcollection_t *
-_gmx_sel_lexer_selcollection(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
- return state->sc;
-}
-
-struct gmx_ana_indexgrps_t *
-_gmx_sel_lexer_indexgrps(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
- return state->grps;
-}
-
-int
-_gmx_sel_lexer_exp_selcount(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
- return state->nexpsel;
-}
-
-const char *
-_gmx_sel_lexer_pselstr(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
- return state->pselstr;
-}
-
-void
-_gmx_sel_lexer_clear_pselstr(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
- state->pselstr[0] = 0;
- state->pslen = 0;
-}
-
-void
-_gmx_sel_lexer_clear_method_stack(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
-
- state->msp = -1;
-}
-
-void
-_gmx_sel_finish_method(yyscan_t scanner)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
-
- if (state->msp >= 0)
- {
- --state->msp;
- }
-}
-
-void
-_gmx_sel_set_lex_input_file(yyscan_t scanner, FILE *fp)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
-
- state->bBuffer = TRUE;
- state->buffer = _gmx_sel_yy_create_buffer(fp, YY_BUF_SIZE, scanner);
- _gmx_sel_yy_switch_to_buffer(state->buffer, scanner);
-}
-
-void
-_gmx_sel_set_lex_input_str(yyscan_t scanner, const char *str)
-{
- gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
-
- if (state->bBuffer)
- {
- _gmx_sel_yy_delete_buffer(state->buffer, scanner);
- }
- state->bBuffer = TRUE;
- state->buffer = _gmx_sel_yy_scan_string(str, scanner);
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Internal header file used by the selection tokenizer.
- */
-#ifndef SELECTION_SCANNER_INTERNAL_H
-#define SELECTION_SCANNER_INTERNAL_H
-
-#include "parser.h"
-
-/** The scanning function generated by Flex. */
-#define YY_DECL int _gmx_sel_yylex(YYSTYPE *yylval, yyscan_t yyscanner)
-YY_DECL;
-
-/* These need to be defined before including scanner_flex.h, because it
- * uses YY_EXTRA_TYPE. But we also need to include it before defining
- * gmx_sel_lexer_t; hence the forward declaration. */
-struct gmx_sel_lexer_t;
-#define YY_EXTRA_TYPE struct gmx_sel_lexer_t *
-
-/* We cannot include scanner_flex.h from the scanner itself, because it
- * seems to break everything. */
-/* And we need to define YY_NO_UNISTD_H here as well, otherwise unistd.h
- * gets included in other files than scanner.c... */
-#ifndef FLEX_SCANNER
-#define YY_NO_UNISTD_H
-#include "scanner_flex.h"
-#endif
-
-/*! \brief
- * Internal data structure for the selection tokenizer state.
- */
-typedef struct gmx_sel_lexer_t
-{
- struct gmx_ana_selcollection_t *sc;
- struct gmx_ana_indexgrps_t *grps;
- int nexpsel;
-
- gmx_bool bInteractive;
- char *inputstr;
- int nalloc_input;
-
- char *pselstr;
- int pslen;
- int nalloc_psel;
-
- struct gmx_ana_selmethod_t **mstack;
- int msp;
- int mstack_alloc;
-
- int neom;
- struct gmx_ana_selparam_t *nextparam;
- gmx_bool bBoolNo;
- struct gmx_ana_selmethod_t *nextmethod;
- int prev_pos_kw;
-
- gmx_bool bMatchOf;
- gmx_bool bMatchBool;
- gmx_bool bCmdStart;
-
- gmx_bool bBuffer;
- YY_BUFFER_STATE buffer;
-} gmx_sel_lexer_t;
-
-/* Because Flex defines yylval, yytext, and yyleng as macros,
- * and this file is included from scanner.l,
- * we cannot have them here as parameter names... */
-/** Internal function for cases where several tokens need to be returned. */
-int
-_gmx_sel_lexer_process_pending(YYSTYPE *, gmx_sel_lexer_t *state);
-/** Internal function that processes identifier tokens. */
-int
-_gmx_sel_lexer_process_identifier(YYSTYPE *, char *, size_t,
- gmx_sel_lexer_t *state);
-/** Internal function to add a token to the pretty-printed selection text. */
-void
-_gmx_sel_lexer_add_token(const char *str, int len, gmx_sel_lexer_t *state);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Definition of \c gmx_ana_selcollection_t.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_COLLECTION_H
-#define SELECTION_COLLECTION_H
-
-#include <typedefs.h>
-
-#include <indexutil.h>
-
-/*! \internal
- * \brief
- * Information for a collection of selections.
- *
- * The functions to deal with the structure are defined in selection.h.
- * The structure is allocated with gmx_ana_selcollection_create() and
- * freed with gmx_ana_selcollection_free().
- * Some default values must then be set with
- * gmx_ana_selcollection_set_refpostype() and
- * gmx_ana_selcollection_set_outpostype().
- *
- * After setting the default values, one or more selections can be parsed
- * with gmx_ana_selcollection_parse_*().
- * At latest at this point, the topology must be set with
- * gmx_ana_selcollection_set_topology() unless
- * gmx_ana_selcollection_requires_top() returns FALSE.
- * Once all selections are parsed, they must be compiled all at once using
- * gmx_ana_selcollection_compile().
- * After these calls, gmx_ana_selcollection_get_count() and
- * gmx_ana_selcollection_get_selections() can be used
- * to get the compiled selections.
- * gmx_ana_selcollection_evaluate() can be used to update the selections for a
- * new frame.
- * gmx_ana_selcollection_evaluate_fin() can be called after all the frames have
- * been processed to restore the selection values back to the ones they were
- * after gmx_ana_selcollection_compile(), i.e., dynamic selections have the
- * maximal index group as their value.
- *
- * At any point, gmx_ana_selcollection_requires_top() can be called to see
- * whether the information provided so far requires loading the topology.
- * gmx_ana_selcollection_print_tree() can be used to print the internal
- * representation of the selections (mostly useful for debugging).
- */
-struct gmx_ana_selcollection_t
-{
- /** Default reference position type for selections. */
- const char *rpost;
- /** Default output position type for selections. */
- const char *spost;
- /** TRUE if \ref POS_MASKONLY should be used for output position evaluation. */
- gmx_bool bMaskOnly;
- /** TRUE if velocities should be evaluated for output positions. */
- gmx_bool bVelocities;
- /** TRUE if forces should be evaluated for output positions. */
- gmx_bool bForces;
- /** TRUE if debugging output should be printed during compilation. */
- gmx_bool bDebugCompile;
-
- /** Root of the selection element tree. */
- struct t_selelem *root;
- /** Number of selections in \p sel. */
- int nr;
- /** Array of compiled selections. */
- struct gmx_ana_selection_t **sel;
- /** Number of variables defined. */
- int nvars;
- /** Selection strings for variables. */
- char **varstrs;
-
- /** Topology for the collection. */
- t_topology *top;
- /** Index group that contains all the atoms. */
- struct gmx_ana_index_t gall;
- /** Position calculation collection used for selection position evaluation. */
- struct gmx_ana_poscalc_coll_t *pcc;
- /** Memory pool used for selection evaluation. */
- struct gmx_sel_mempool_t *mempool;
- /** Parser symbol table. */
- struct gmx_sel_symtab_t *symtab;
-};
-
-/** Clears the symbol table in the collection */
-void
-_gmx_selcollection_clear_symtab(struct gmx_ana_selcollection_t *sc);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief
- * Implementation of functions in selection.h.
- */
-/*! \internal \dir src/gmxlib/selection
- * \brief
- * Source code for selection-related routines.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <smalloc.h>
-#include <statutil.h>
-#include <string2.h>
-#include <xvgr.h>
-#include <gmx_fatal.h>
-
-#include <poscalc.h>
-#include <selection.h>
-#include <selmethod.h>
-
-#include "mempool.h"
-#include "selcollection.h"
-#include "selelem.h"
-#include "symrec.h"
-
-/*!
- * \param[out] scp Pointer to a newly allocated empty selection collection.
- * \param[in] pcc Position calculation data structure to use for selection
- * position evaluation.
- * \returns 0 on success.
- */
-int
-gmx_ana_selcollection_create(gmx_ana_selcollection_t **scp,
- gmx_ana_poscalc_coll_t *pcc)
-{
- gmx_ana_selcollection_t *sc;
-
- snew(sc, 1);
- sc->rpost = NULL;
- sc->spost = NULL;
- sc->bMaskOnly = FALSE;
- sc->bVelocities = FALSE;
- sc->bForces = FALSE;
- sc->bDebugCompile = FALSE;
- sc->root = NULL;
- sc->nr = 0;
- sc->sel = NULL;
- sc->nvars = 0;
- sc->varstrs = NULL;
- sc->top = NULL;
- gmx_ana_index_clear(&sc->gall);
- sc->pcc = pcc;
- sc->mempool = NULL;
- _gmx_sel_symtab_create(&sc->symtab);
- *scp = sc;
- return 0;
-}
-
-/*!
- * \param[in,out] sc Selection collection to free.
- *
- * The pointer \p sc is invalid after the call.
- */
-void
-gmx_ana_selcollection_free(gmx_ana_selcollection_t *sc)
-{
- int i;
-
- _gmx_selelem_free_chain(sc->root);
- if (sc->sel)
- {
- for (i = 0; i < sc->nr; ++i)
- {
- gmx_ana_selection_free(sc->sel[i]);
- }
- }
- sfree(sc->sel);
- for (i = 0; i < sc->nvars; ++i)
- {
- sfree(sc->varstrs[i]);
- }
- sfree(sc->varstrs);
- gmx_ana_index_deinit(&sc->gall);
- if (sc->mempool)
- {
- _gmx_sel_mempool_destroy(sc->mempool);
- }
- _gmx_selcollection_clear_symtab(sc);
- sfree(sc);
-}
-
-/*!
- * \param[in,out] sc Selection collection.
- */
-void
-_gmx_selcollection_clear_symtab(gmx_ana_selcollection_t *sc)
-{
- if (sc->symtab)
- {
- _gmx_sel_symtab_free(sc->symtab);
- sc->symtab = NULL;
- }
-}
-
-/*!
- * \param[in,out] sc Selection collection to modify.
- * \param[in] type Default selection reference position type
- * (one of the strings acceptable for gmx_ana_poscalc_type_from_enum()).
- *
- * Should be called before calling gmx_ana_selcollection_requires_top() or
- * gmx_ana_selcollection_parse_*().
- */
-void
-gmx_ana_selcollection_set_refpostype(gmx_ana_selcollection_t *sc,
- const char *type)
-{
- sc->rpost = type;
-}
-
-/*!
- * \param[in,out] sc Selection collection to modify.
- * \param[in] type Default selection output position type
- * (one of the strings acceptable for gmx_ana_poslcalc_type_from_enum()).
- * \param[in] bMaskOnly If TRUE, the output positions are initialized
- * using \ref POS_MASKONLY.
- *
- * If \p type is NULL, the default type is not modified.
- * Should be called before calling gmx_ana_selcollection_requires_top() or
- * gmx_ana_selcollection_parse_*().
- */
-void
-gmx_ana_selcollection_set_outpostype(gmx_ana_selcollection_t *sc,
- const char *type, gmx_bool bMaskOnly)
-{
- if (type)
- {
- sc->spost = type;
- }
- sc->bMaskOnly = bMaskOnly;
-}
-
-/*!
- * \param[in,out] sc Selection collection to modify.
- * \param[in] bVelOut If TRUE, selections will also evaluate
- * velocities.
- */
-void
-gmx_ana_selcollection_set_veloutput(gmx_ana_selcollection_t *sc,
- gmx_bool bVelOut)
-{
- sc->bVelocities = bVelOut;
-}
-
-/*!
- * \param[in,out] sc Selection collection to modify.
- * \param[in] bForceOut If TRUE, selections will also evaluate
- * forces.
- */
-void
-gmx_ana_selcollection_set_forceoutput(gmx_ana_selcollection_t *sc,
- gmx_bool bForceOut)
-{
- sc->bForces = bForceOut;
-}
-
-/*!
- * \param[in,out] sc Selection collection to set the topology for.
- * \param[in] top Topology data.
- * \param[in] natoms Number of atoms. If <=0, the number of atoms in the
- * topology is used.
- * \returns 0 on success, EINVAL if \p top is NULL and \p natoms <= 0.
- *
- * The topology is also set for the position calculation collection
- * associated with \p sc.
- *
- * \p natoms determines the largest atom index that can be selected by the
- * selection: even if the topology contains more atoms, they will not be
- * selected.
- */
-int
-gmx_ana_selcollection_set_topology(gmx_ana_selcollection_t *sc, t_topology *top,
- int natoms)
-{
- gmx_ana_poscalc_coll_set_topology(sc->pcc, top);
- sc->top = top;
-
- /* Get the number of atoms from the topology if it is not given */
- if (natoms <= 0)
- {
- if (!sc->top)
- {
- gmx_incons("selections need either the topology or the number of atoms");
- return EINVAL;
- }
- natoms = sc->top->atoms.nr;
- }
- gmx_ana_index_init_simple(&sc->gall, natoms, NULL);
- return 0;
-}
-
-/*!
- * \param[in] sc Selection collection to query.
- * \returns Number of selections in \p sc.
- *
- * If gmx_ana_selcollection_parse_*() has not been called, returns 0.
- *
- * \see gmx_ana_selcollection_get_selection()
- */
-int
-gmx_ana_selcollection_get_count(gmx_ana_selcollection_t *sc)
-{
- return sc->nr;
-}
-
-/*!
- * \param[in] sc Selection collection to query.
- * \param[in] i Number of the selection.
- * \returns Pointer to the \p i'th selection in \p sc,
- * or NULL if there is no such selection.
- *
- * \p i should be between 0 and the value returned by
- * gmx_ana_selcollection_get_count().
- * The returned pointer should not be freed.
- * If gmx_ana_selcollection_compile() has not been called, the returned
- * selection is not completely initialized (but the returned pointer will be
- * valid even after compilation, and will point to the initialized selection).
- *
- * \see gmx_ana_selcollection_get_count()
- */
-gmx_ana_selection_t *
-gmx_ana_selcollection_get_selection(gmx_ana_selcollection_t *sc, int i)
-{
- if (i < 0 || i >= sc->nr || !sc->sel)
- return NULL;
- return sc->sel[i];
-}
-
-/*!
- * \param[in] sc Selection collection to query.
- * \returns TRUE if any selection in \p sc requires topology information,
- * FALSE otherwise.
- *
- * Before gmx_ana_selcollection_parse_*(), the return value is based just on
- * the position types set.
- * After gmx_ana_selcollection_parse_*(), the return value also takes into account the
- * selection keywords used.
- */
-gmx_bool
-gmx_ana_selcollection_requires_top(gmx_ana_selcollection_t *sc)
-{
- t_selelem *sel;
- e_poscalc_t type;
- int flags;
- int rc;
-
- if (sc->rpost)
- {
- flags = 0;
- rc = gmx_ana_poscalc_type_from_enum(sc->rpost, &type, &flags);
- if (rc == 0 && type != POS_ATOM)
- {
- return TRUE;
- }
- }
- if (sc->spost)
- {
- flags = 0;
- rc = gmx_ana_poscalc_type_from_enum(sc->spost, &type, &flags);
- if (rc == 0 && type != POS_ATOM)
- {
- return TRUE;
- }
- }
-
- sel = sc->root;
- while (sel)
- {
- if (_gmx_selelem_requires_top(sel))
- {
- return TRUE;
- }
- sel = sel->next;
- }
- return FALSE;
-}
-
-/*!
- * \param[in] fp File handle to receive the output.
- * \param[in] sc Selection collection to print.
- * \param[in] bValues If TRUE, the evaluated values of selection elements
- * are printed as well.
- */
-void
-gmx_ana_selcollection_print_tree(FILE *fp, gmx_ana_selcollection_t *sc, gmx_bool bValues)
-{
- t_selelem *sel;
-
- sel = sc->root;
- while (sel)
- {
- _gmx_selelem_print_tree(fp, sel, bValues, 0);
- sel = sel->next;
- }
-}
-
-/*!
- * \param[in] sel Selection to free.
- *
- * After the call, the pointer \p sel is invalid.
- */
-void
-gmx_ana_selection_free(gmx_ana_selection_t *sel)
-{
- sfree(sel->name);
- sfree(sel->selstr);
- gmx_ana_pos_deinit(&sel->p);
- if (sel->m != sel->orgm)
- {
- sfree(sel->m);
- }
- if (sel->q != sel->orgq)
- {
- sfree(sel->q);
- }
- sfree(sel->orgm);
- sfree(sel->orgq);
- sfree(sel);
-}
-
-/*!
- * \param[in] sel Selection whose name is needed.
- * \returns Pointer to the name of the selection.
- *
- * The return value should not be freed by the caller.
- */
-char *
-gmx_ana_selection_name(gmx_ana_selection_t *sel)
-{
- return sel->name;
-}
-
-/*!
- * \param[in] sel Selection for which information should be printed.
- */
-void
-gmx_ana_selection_print_info(gmx_ana_selection_t *sel)
-{
- fprintf(stderr, "\"%s\" (%d position%s, %d atom%s%s)", sel->name,
- sel->p.nr, sel->p.nr == 1 ? "" : "s",
- sel->g->isize, sel->g->isize == 1 ? "" : "s",
- sel->bDynamic ? ", dynamic" : "");
- fprintf(stderr, "\n");
-}
-
-/*!
- * \param[in] sel Selection to initialize.
- * \param[in] type Type of covered fraction required.
- * \returns TRUE if the covered fraction can be calculated for the selection,
- * FALSE otherwise.
- */
-gmx_bool
-gmx_ana_selection_init_coverfrac(gmx_ana_selection_t *sel, e_coverfrac_t type)
-{
- sel->cfractype = type;
- if (type == CFRAC_NONE || !sel->selelem)
- {
- sel->bCFracDyn = FALSE;
- }
- else if (!_gmx_selelem_can_estimate_cover(sel->selelem))
- {
- sel->cfractype = CFRAC_NONE;
- sel->bCFracDyn = FALSE;
- }
- else
- {
- sel->bCFracDyn = TRUE;
- }
- sel->cfrac = sel->bCFracDyn ? 0.0 : 1.0;
- sel->avecfrac = sel->cfrac;
- return type == CFRAC_NONE || sel->cfractype != CFRAC_NONE;
-}
-
-/*!
- * \param[in] out Output file.
- * \param[in] sc Selection collection which should be written.
- * \param[in] oenv Output options structure.
- */
-void xvgr_selcollection(FILE *out, gmx_ana_selcollection_t *sc,
- const output_env_t oenv)
-{
- int i;
-
- if (output_env_get_xvg_format(oenv) != exvgNONE && sc)
- {
- fprintf(out, "# Selections:\n");
- for (i = 0; i < sc->nvars; ++i)
- {
- fprintf(out, "# %s\n", sc->varstrs[i]);
- }
- for (i = 0; i < sc->nr; ++i)
- {
- fprintf(out, "# %s\n", sc->sel[i]->selstr);
- }
- fprintf(out, "#\n");
- }
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in selelem.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <smalloc.h>
-#include <gmx_fatal.h>
-
-#include <indexutil.h>
-#include <poscalc.h>
-#include <position.h>
-#include <selmethod.h>
-
-#include "keywords.h"
-#include "mempool.h"
-#include "selelem.h"
-
-/*!
- * \param[in] sel Selection for which the string is requested
- * \returns Pointer to a string that corresponds to \p sel->type.
- *
- * The return value points to a string constant and should not be \p free'd.
- *
- * The function returns NULL if \p sel->type is not one of the valid values.
- */
-const char *
-_gmx_selelem_type_str(t_selelem *sel)
-{
- switch (sel->type)
- {
- case SEL_CONST: return "CONST";
- case SEL_EXPRESSION: return "EXPR";
- case SEL_BOOLEAN: return "BOOL";
- case SEL_ARITHMETIC: return "ARITH";
- case SEL_ROOT: return "ROOT";
- case SEL_SUBEXPR: return "SUBEXPR";
- case SEL_SUBEXPRREF: return "REF";
- case SEL_MODIFIER: return "MODIFIER";
- }
- return NULL;
-}
-
-/*!
- * \param[in] val Value structore for which the string is requested.
- * \returns Pointer to a string that corresponds to \p val->type,
- * NULL if the type value is invalid.
- *
- * The return value points to a string constant and should not be \p free'd.
- */
-const char *
-_gmx_sel_value_type_str(gmx_ana_selvalue_t *val)
-{
- switch (val->type)
- {
- case NO_VALUE: return "NONE";
- case INT_VALUE: return "INT";
- case REAL_VALUE: return "REAL";
- case STR_VALUE: return "STR";
- case POS_VALUE: return "VEC";
- case GROUP_VALUE: return "GROUP";
- }
- return NULL;
-}
-
-/*! \copydoc _gmx_selelem_type_str() */
-const char *
-_gmx_selelem_gmx_boolean_type_str(t_selelem *sel)
-{
- switch (sel->u.boolt)
- {
- case BOOL_NOT: return "NOT"; break;
- case BOOL_AND: return "AND"; break;
- case BOOL_OR: return "OR"; break;
- case BOOL_XOR: return "XOR"; break;
- }
- return NULL;
-}
-
-/*!
- * \param[in] type Type of selection element to allocate.
- * \returns Pointer to the newly allocated and initialized element.
- *
- * \c t_selelem::type is set to \p type,
- * \c t_selelem::v::type is set to \ref GROUP_VALUE for gmx_boolean and comparison
- * expressions and \ref NO_VALUE for others,
- * \ref SEL_ALLOCVAL is set for non-root elements (\ref SEL_ALLOCDATA is also
- * set for \ref SEL_BOOLEAN elements),
- * and \c t_selelem::refcount is set to one.
- * All the pointers are set to NULL.
- */
-t_selelem *
-_gmx_selelem_create(e_selelem_t type)
-{
- t_selelem *sel;
-
- snew(sel, 1);
- sel->name = NULL;
- sel->type = type;
- sel->flags = (type != SEL_ROOT) ? SEL_ALLOCVAL : 0;
- if (type == SEL_BOOLEAN)
- {
- sel->v.type = GROUP_VALUE;
- sel->flags |= SEL_ALLOCDATA;
- }
- else
- {
- sel->v.type = NO_VALUE;
- }
- _gmx_selvalue_clear(&sel->v);
- sel->evaluate = NULL;
- sel->mempool = NULL;
- sel->child = NULL;
- sel->next = NULL;
- sel->refcount = 1;
-
- return sel;
-}
-
-/*!
- * \param[in,out] sel Selection element to set the type for.
- * \param[in] vtype Value type for the selection element.
- * \returns 0 on success, EINVAL if the value type is invalid.
- *
- * If the new type is \ref GROUP_VALUE or \ref POS_VALUE, the
- * \ref SEL_ALLOCDATA flag is also set.
- *
- * This function should only be called at most once for each element,
- * preferably right after calling _gmx_selelem_create().
- */
-int
-_gmx_selelem_set_vtype(t_selelem *sel, e_selvalue_t vtype)
-{
- if (sel->type == SEL_BOOLEAN && vtype != GROUP_VALUE)
- {
- gmx_bug("internal error");
- return EINVAL;
- }
- if (sel->v.type != NO_VALUE && vtype != sel->v.type)
- {
- gmx_call("_gmx_selelem_set_vtype() called more than once");
- return EINVAL;
- }
- sel->v.type = vtype;
- if (vtype == GROUP_VALUE || vtype == POS_VALUE)
- {
- sel->flags |= SEL_ALLOCDATA;
- }
- return 0;
-}
-
-/*!
- * \param[in,out] sel Selection element to reserve.
- * \param[in] count Number of values to reserve memory for.
- * \returns 0 on success or if no memory pool, non-zero on error.
- *
- * Reserves memory for the values of \p sel from the \p sel->mempool
- * memory pool. If no memory pool is set, nothing is done.
- */
-int
-_gmx_selelem_mempool_reserve(t_selelem *sel, int count)
-{
- int rc = 0;
-
- if (!sel->mempool)
- {
- return 0;
- }
- switch (sel->v.type)
- {
- case INT_VALUE:
- rc = _gmx_sel_mempool_alloc(sel->mempool, (void **)&sel->v.u.i,
- sizeof(*sel->v.u.i)*count);
- break;
-
- case REAL_VALUE:
- rc = _gmx_sel_mempool_alloc(sel->mempool, (void **)&sel->v.u.r,
- sizeof(*sel->v.u.r)*count);
- break;
-
- case GROUP_VALUE:
- rc = _gmx_sel_mempool_alloc_group(sel->mempool, sel->v.u.g, count);
- break;
-
- default:
- gmx_incons("mem pooling not implemented for requested type");
- return -1;
- }
- return rc;
-}
-
-/*!
- * \param[in,out] sel Selection element to release.
- *
- * Releases the memory allocated for the values of \p sel from the
- * \p sel->mempool memory pool. If no memory pool is set, nothing is done.
- */
-void
-_gmx_selelem_mempool_release(t_selelem *sel)
-{
- if (!sel->mempool)
- {
- return;
- }
- switch (sel->v.type)
- {
- case INT_VALUE:
- case REAL_VALUE:
- _gmx_sel_mempool_free(sel->mempool, sel->v.u.ptr);
- _gmx_selvalue_setstore(&sel->v, NULL);
- break;
-
- case GROUP_VALUE:
- if (sel->v.u.g)
- {
- _gmx_sel_mempool_free_group(sel->mempool, sel->v.u.g);
- }
- break;
-
- default:
- gmx_incons("mem pooling not implemented for requested type");
- break;
- }
-}
-
-/*!
- * \param[in] sel Selection to free.
- */
-void
-_gmx_selelem_free_values(t_selelem *sel)
-{
- int i, n;
-
- _gmx_selelem_mempool_release(sel);
- if ((sel->flags & SEL_ALLOCDATA) && sel->v.u.ptr)
- {
- /* The number of position/group structures is constant, so the
- * backup of using sel->v.nr should work for them.
- * For strings, we report an error if we don't know the allocation
- * size here. */
- n = (sel->v.nalloc > 0) ? sel->v.nalloc : sel->v.nr;
- switch (sel->v.type)
- {
- case STR_VALUE:
- if (sel->v.nalloc == 0)
- {
- gmx_bug("SEL_ALLOCDATA should only be set for allocated STR_VALUE values");
- break;
- }
- for (i = 0; i < n; ++i)
- {
- sfree(sel->v.u.s[i]);
- }
- break;
- case POS_VALUE:
- for (i = 0; i < n; ++i)
- {
- gmx_ana_pos_deinit(&sel->v.u.p[i]);
- }
- break;
- case GROUP_VALUE:
- for (i = 0; i < n; ++i)
- {
- gmx_ana_index_deinit(&sel->v.u.g[i]);
- }
- break;
- default: /* No special handling for other types */
- break;
- }
- }
- if (sel->flags & SEL_ALLOCVAL)
- {
- sfree(sel->v.u.ptr);
- }
- _gmx_selvalue_setstore(&sel->v, NULL);
- if (sel->type == SEL_SUBEXPRREF && sel->u.param)
- {
- sel->u.param->val.u.ptr = NULL;
- }
-}
-
-/*!
- * \param[in] method Method to free.
- * \param[in] mdata Method data to free.
- */
-void
-_gmx_selelem_free_method(gmx_ana_selmethod_t *method, void *mdata)
-{
- sel_freefunc free_func = NULL;
-
- /* Save the pointer to the free function. */
- if (method && method->free)
- {
- free_func = method->free;
- }
-
- /* Free the method itself.
- * Has to be done before freeing the method data, because parameter
- * values are typically stored in the method data, and here we may
- * access them. */
- if (method)
- {
- int i, j;
-
- /* Free the memory allocated for the parameters that are not managed
- * by the selection method itself. */
- for (i = 0; i < method->nparams; ++i)
- {
- gmx_ana_selparam_t *param = &method->param[i];
-
- if (param->val.u.ptr)
- {
- if (param->val.type == GROUP_VALUE)
- {
- for (j = 0; j < param->val.nr; ++j)
- {
- gmx_ana_index_deinit(¶m->val.u.g[j]);
- }
- }
- else if (param->val.type == POS_VALUE)
- {
- for (j = 0; j < param->val.nr; ++j)
- {
- gmx_ana_pos_deinit(¶m->val.u.p[j]);
- }
- }
-
- if (param->val.nalloc > 0)
- {
- sfree(param->val.u.ptr);
- }
- }
- }
- sfree(method->param);
- sfree(method);
- }
- /* Free method data. */
- if (mdata)
- {
- if (free_func)
- {
- free_func(mdata);
- }
- sfree(mdata);
- }
-}
-
-/*!
- * \param[in] sel Selection to free.
- */
-void
-_gmx_selelem_free_exprdata(t_selelem *sel)
-{
- if (sel->type == SEL_EXPRESSION || sel->type == SEL_MODIFIER)
- {
- _gmx_selelem_free_method(sel->u.expr.method, sel->u.expr.mdata);
- sel->u.expr.mdata = NULL;
- sel->u.expr.method = NULL;
- /* Free position data */
- if (sel->u.expr.pos)
- {
- gmx_ana_pos_free(sel->u.expr.pos);
- sel->u.expr.pos = NULL;
- }
- /* Free position calculation data */
- if (sel->u.expr.pc)
- {
- gmx_ana_poscalc_free(sel->u.expr.pc);
- sel->u.expr.pc = NULL;
- }
- }
- if (sel->type == SEL_ARITHMETIC)
- {
- sfree(sel->u.arith.opstr);
- sel->u.arith.opstr = NULL;
- }
- if (sel->type == SEL_SUBEXPR || sel->type == SEL_ROOT
- || (sel->type == SEL_CONST && sel->v.type == GROUP_VALUE))
- {
- gmx_ana_index_deinit(&sel->u.cgrp);
- }
-}
-
-/*!
- * \param[in] sel Selection to free.
- *
- * Decrements \ref t_selelem::refcount "sel->refcount" and frees the
- * memory allocated for \p sel and all its children if the reference count
- * reaches zero.
- */
-void
-_gmx_selelem_free(t_selelem *sel)
-{
- /* Decrement the reference counter and do nothing if references remain */
- sel->refcount--;
- if (sel->refcount > 0)
- {
- return;
- }
-
- /* Free the children.
- * Must be done before freeing other data, because the children may hold
- * references to data in this element. */
- _gmx_selelem_free_chain(sel->child);
-
- /* Free value storage */
- _gmx_selelem_free_values(sel);
-
- /* Free other storage */
- _gmx_selelem_free_exprdata(sel);
-
- /* Free temporary compiler data if present */
- _gmx_selelem_free_compiler_data(sel);
-
- sfree(sel);
-}
-
-/*!
- * \param[in] first First selection to free.
- *
- * Frees \p first and all selections accessible through the
- * \ref t_selelem::next "first->next" pointer.
- */
-void
-_gmx_selelem_free_chain(t_selelem *first)
-{
- t_selelem *child, *prev;
-
- child = first;
- while (child)
- {
- prev = child;
- child = child->next;
- _gmx_selelem_free(prev);
- }
-}
-
-/*!
- * \param[in] fp File handle to receive the output.
- * \param[in] sel Root of the selection subtree to print.
- * \param[in] bValues If TRUE, the evaluated values of selection elements
- * are printed as well.
- * \param[in] level Indentation level, starting from zero.
- */
-void
-_gmx_selelem_print_tree(FILE *fp, t_selelem *sel, gmx_bool bValues, int level)
-{
- t_selelem *child;
- int i;
-
- fprintf(fp, "%*c %s %s", level*2+1, '*',
- _gmx_selelem_type_str(sel), _gmx_sel_value_type_str(&sel->v));
- if (sel->name)
- {
- fprintf(fp, " \"%s\"", sel->name);
- }
- fprintf(fp, " flg=");
- if (sel->flags & SEL_FLAGSSET)
- {
- fprintf(fp, "s");
- }
- if (sel->flags & SEL_SINGLEVAL)
- {
- fprintf(fp, "S");
- }
- if (sel->flags & SEL_ATOMVAL)
- {
- fprintf(fp, "A");
- }
- if (sel->flags & SEL_VARNUMVAL)
- {
- fprintf(fp, "V");
- }
- if (sel->flags & SEL_DYNAMIC)
- {
- fprintf(fp, "D");
- }
- if (!(sel->flags & SEL_VALFLAGMASK))
- {
- fprintf(fp, "0");
- }
- if (sel->mempool)
- {
- fprintf(fp, "P");
- }
- if (sel->type == SEL_CONST)
- {
- if (sel->v.type == INT_VALUE)
- {
- fprintf(fp, " %d", sel->v.u.i[0]);
- }
- else if (sel->v.type == REAL_VALUE)
- {
- fprintf(fp, " %f", sel->v.u.r[0]);
- }
- else if (sel->v.type == GROUP_VALUE)
- {
- gmx_ana_index_t *g = sel->v.u.g;
- if (!g || g->isize == 0)
- g = &sel->u.cgrp;
- fprintf(fp, " (%d atoms)", g->isize);
- }
- }
- else if (sel->type == SEL_BOOLEAN)
- {
- fprintf(fp, " %s", _gmx_selelem_gmx_boolean_type_str(sel));
- }
- else if (sel->type == SEL_EXPRESSION
- && sel->u.expr.method->name == sm_compare.name)
- {
- _gmx_selelem_print_compare_info(fp, sel->u.expr.mdata);
- }
- if (sel->evaluate)
- {
- fprintf(fp, " eval=");
- _gmx_sel_print_evalfunc_name(fp, sel->evaluate);
- }
- if (sel->refcount > 1)
- {
- fprintf(fp, " refc=%d", sel->refcount);
- }
- if (!(sel->flags & SEL_ALLOCVAL))
- {
- fprintf(fp, " (ext. output)");
- }
- fprintf(fp, "\n");
-
- if ((sel->type == SEL_CONST && sel->v.type == GROUP_VALUE) || sel->type == SEL_ROOT)
- {
- gmx_ana_index_t *g = sel->v.u.g;
- if (!g || g->isize == 0 || sel->evaluate != NULL)
- {
- g = &sel->u.cgrp;
- }
- if (g->isize < 0)
- {
- fprintf(fp, "%*c group: (null)\n", level*2+1, ' ');
- }
- else if (g->isize > 0)
- {
- fprintf(fp, "%*c group:", level*2+1, ' ');
- if (g->isize <= 20)
- {
- for (i = 0; i < g->isize; ++i)
- {
- fprintf(fp, " %d", g->index[i] + 1);
- }
- }
- else
- {
- fprintf(fp, " %d atoms", g->isize);
- }
- fprintf(fp, "\n");
- }
- }
- else if (sel->type == SEL_EXPRESSION)
- {
- if (sel->u.expr.pc)
- {
- fprintf(fp, "%*c COM", level*2+3, '*');
- fprintf(fp, "\n");
- }
- }
-
- if (sel->cdata)
- {
- _gmx_selelem_print_compiler_info(fp, sel, level);
- }
-
- if (bValues && sel->type != SEL_CONST && sel->type != SEL_ROOT && sel->v.u.ptr)
- {
- fprintf(fp, "%*c value: ", level*2+1, ' ');
- switch (sel->v.type)
- {
- case POS_VALUE:
- /* In normal use, the pointer should never be NULL, but it's
- * useful to have the check for debugging to avoid accidental
- * segfaults when printing the selection tree. */
- if (sel->v.u.p->x)
- {
- fprintf(fp, "(%f, %f, %f)",
- sel->v.u.p->x[0][XX], sel->v.u.p->x[0][YY],
- sel->v.u.p->x[0][ZZ]);
- }
- else
- {
- fprintf(fp, "(null)");
- }
- break;
- case GROUP_VALUE:
- fprintf(fp, "%d atoms", sel->v.u.g->isize);
- if (sel->v.u.g->isize < 20)
- {
- if (sel->v.u.g->isize > 0)
- {
- fprintf(fp, ":");
- }
- for (i = 0; i < sel->v.u.g->isize; ++i)
- {
- fprintf(fp, " %d", sel->v.u.g->index[i] + 1);
- }
- }
- break;
- default:
- fprintf(fp, "???");
- break;
- }
- fprintf(fp, "\n");
- }
-
- /* Print the subexpressions with one more level of indentation */
- child = sel->child;
- while (child)
- {
- if (!(sel->type == SEL_SUBEXPRREF && child->type == SEL_SUBEXPR))
- {
- _gmx_selelem_print_tree(fp, child, bValues, level+1);
- }
- child = child->next;
- }
-}
-
-/*!
- * \param[in] root Root of the subtree to query.
- * \returns TRUE if \p root or any any of its elements require topology
- * information, FALSE otherwise.
- */
-gmx_bool
-_gmx_selelem_requires_top(t_selelem *root)
-{
- t_selelem *child;
-
- if (root->type == SEL_EXPRESSION || root->type == SEL_MODIFIER)
- {
- if (root->u.expr.method && (root->u.expr.method->flags & SMETH_REQTOP))
- {
- return TRUE;
- }
- if (root->u.expr.pc && gmx_ana_poscalc_requires_top(root->u.expr.pc))
- {
- return TRUE;
- }
- }
- child = root->child;
- while (child)
- {
- if (_gmx_selelem_requires_top(child))
- {
- return TRUE;
- }
- child = child->next;
- }
- return FALSE;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Definition of \c t_selelem and related things.
- *
- * The selection element trees constructed by the parser and the compiler
- * are described on the respective pages:
- * \ref selparser and \ref selcompiler.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_ELEMENT_H
-#define SELECTION_ELEMENT_H
-
-#include <types/simple.h>
-
-#include <indexutil.h>
-#include <selvalue.h>
-
-struct gmx_ana_poscalc_t;
-struct gmx_ana_selparam_t;
-struct gmx_ana_selmethod_t;
-
-struct gmx_sel_evaluate_t;
-struct gmx_sel_mempool_t;
-struct t_selelem;
-
-/********************************************************************/
-/*! \name Enumerations for expression types
- ********************************************************************/
-/*@{*/
-
-/** Defines the type of a \c t_selelem object. */
-typedef enum
-{
- /** Constant-valued expression. */
- SEL_CONST,
- /** Method expression that requires evaluation. */
- SEL_EXPRESSION,
- /** Boolean expression. */
- SEL_BOOLEAN,
- /** Arithmetic expression. */
- SEL_ARITHMETIC,
- /** Root node of the evaluation tree. */
- SEL_ROOT,
- /** Subexpression that may be referenced several times. */
- SEL_SUBEXPR,
- /** Reference to a subexpression. */
- SEL_SUBEXPRREF,
- /** Post-processing of selection value. */
- SEL_MODIFIER
-} e_selelem_t;
-
-/** Defines the gmx_boolean operation of \c t_selelem objects with type \ref SEL_BOOLEAN. */
-typedef enum
-{
- BOOL_NOT, /**< Not */
- BOOL_AND, /**< And */
- BOOL_OR, /**< Or */
- BOOL_XOR /**< Xor (not implemented). */
-} e_boolean_t;
-
-/** Defines the arithmetic operation of \c t_selelem objects with type \ref SEL_ARITHMETIC. */
-typedef enum
-{
- ARITH_PLUS, /**< + */
- ARITH_MINUS, /**< - */
- ARITH_NEG, /**< Unary - */
- ARITH_MULT, /**< * */
- ARITH_DIV, /**< / */
- ARITH_EXP /**< ^ (to power) */
-} e_arithmetic_t;
-
-/** Returns a string representation of the type of a \c t_selelem. */
-extern const char *
-_gmx_selelem_type_str(struct t_selelem *sel);
-/** Returns a string representation of the gmx_boolean type of a \ref SEL_BOOLEAN \c t_selelem. */
-extern const char *
-_gmx_selelem_gmx_boolean_type_str(struct t_selelem *sel);
-/** Returns a string representation of the type of a \c gmx_ana_selvalue_t. */
-extern const char *
-_gmx_sel_value_type_str(gmx_ana_selvalue_t *val);
-
-/*@}*/
-
-
-/********************************************************************/
-/*! \name Selection expression flags
- * \anchor selelem_flags
- ********************************************************************/
-/*@{*/
-/*! \brief
- * Selection value flags are set.
- *
- * If this flag is set, the flags covered by \ref SEL_VALFLAGMASK
- * have been set properly for the element.
- */
-#define SEL_FLAGSSET 1
-/*! \brief
- * The element evaluates to a single value.
- *
- * This flag is always set for \ref GROUP_VALUE elements.
- */
-#define SEL_SINGLEVAL 2
-/*! \brief
- * The element evaluates to one value for each input atom.
- */
-#define SEL_ATOMVAL 4
-/*! \brief
- * The element evaluates to an arbitrary number of values.
- */
-#define SEL_VARNUMVAL 8
-/*! \brief
- * The element (or one of its children) is dynamic.
- */
-#define SEL_DYNAMIC 16
-/*! \brief
- * Mask that covers the flags that describe the number of values.
- */
-#define SEL_VALTYPEMASK (SEL_SINGLEVAL | SEL_ATOMVAL | SEL_VARNUMVAL)
-/*! \brief
- * Mask that covers the flags that describe the value type.
- */
-#define SEL_VALFLAGMASK (SEL_FLAGSSET | SEL_VALTYPEMASK | SEL_DYNAMIC)
-/*! \brief
- * Data has been allocated for the \p v.u union.
- *
- * If not set, the \p v.u.ptr points to data allocated externally.
- * This is the case if the value of the element is used as a parameter
- * for a selection method or if the element evaluates the final value of
- * a selection.
- *
- * Even if the flag is set, \p v.u.ptr can be NULL during initialization.
- *
- * \todo
- * This flag overlaps with the function of \p v.nalloc field, and could
- * probably be removed, making memory management simpler. Currently, the
- * \p v.nalloc field is not kept up-to-date in all cases when this flag
- * is changed and is used in places where this flag is not, so this would
- * require a careful investigation of the selection code.
- */
-#define SEL_ALLOCVAL (1<<8)
-/*! \brief
- * Data has been allocated for the group/position structure.
- *
- * If not set, the memory allocated for fields in \p v.u.g or \p v.u.p is
- * managed externally.
- *
- * This field has no effect if the value type is not \ref GROUP_VALUE or
- * \ref POS_VALUE, but should not be set.
- */
-#define SEL_ALLOCDATA (1<<9)
-/*! \brief
- * \p method->init_frame should be called for the frame.
- */
-#define SEL_INITFRAME (1<<10)
-/*! \brief
- * Parameter has been evaluated for the current frame.
- *
- * This flag is set for children of \ref SEL_EXPRESSION elements (which
- * describe method parameters) after the element has been evaluated for the
- * current frame.
- * It is not set for \ref SEL_ATOMVAL elements, because they may need to
- * be evaluated multiple times.
- */
-#define SEL_EVALFRAME (1<<11)
-/*! \brief
- * \p method->init has been called.
- */
-#define SEL_METHODINIT (1<<12)
-/*! \brief
- * \p method->outinit has been called.
- *
- * This flag is also used for \ref SEL_SUBEXPRREF elements.
- */
-#define SEL_OUTINIT (1<<13)
-/*@}*/
-
-
-/********************************************************************/
-/*! \name Selection expression data structures and functions
- ********************************************************************/
-/*@{*/
-
-struct t_selelem;
-
-/*! \brief
- * Function pointer for evaluating a \c t_selelem.
- */
-typedef int (*sel_evalfunc)(struct gmx_sel_evaluate_t *data,
- struct t_selelem *sel, gmx_ana_index_t *g);
-
-/*! \internal \brief
- * Represents an element of a selection expression.
- */
-typedef struct t_selelem
-{
- /*! \brief Name of the element.
- *
- * This field is only used for informative purposes.
- * It is always either NULL or a pointer to a string.
- * Memory is never allocated for it directly.
- */
- const char *name;
- /** Type of the element. */
- e_selelem_t type;
- /*! \brief
- * Value storage of the element.
- *
- * This field contains the evaluated value of the element, as well as
- * the output value type.
- */
- gmx_ana_selvalue_t v;
- /*! \brief
- * Evaluation function for the element.
- *
- * Can be either NULL (if the expression is a constant and does not require
- * evaluation) or point to one of the functions defined in evaluate.h.
- */
- sel_evalfunc evaluate;
- /*! \brief
- * Information flags about the element.
- *
- * Allowed flags are listed here:
- * \ref selelem_flags "flags for \c t_selelem".
- */
- int flags;
- /** Data required by the evaluation function. */
- union {
- /*! \brief Index group data for several element types.
- *
- * - \ref SEL_CONST : if the value type is \ref GROUP_VALUE,
- * this field holds the unprocessed group value.
- * - \ref SEL_ROOT : holds the group value for which the
- * selection subtree should be evaluated.
- * - \ref SEL_SUBEXPR : holds the group for which the subexpression
- * has been evaluated.
- */
- gmx_ana_index_t cgrp;
- /** Data for \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements. */
- struct {
- /** Pointer the the method used in this expression. */
- struct gmx_ana_selmethod_t *method;
- /** Pointer to the data allocated by the method's \p init_data (see sel_datafunc()). */
- void *mdata;
- /** Pointer to the position data passed to the method. */
- struct gmx_ana_pos_t *pos;
- /** Pointer to the evaluation data for \p pos. */
- struct gmx_ana_poscalc_t *pc;
- } expr;
- /** Operation type for \ref SEL_BOOLEAN elements. */
- e_boolean_t boolt;
- /** Operation type for \ref SEL_ARITHMETIC elements. */
- struct {
- /** Operation type. */
- e_arithmetic_t type;
- /** String representation. */
- char *opstr;
- } arith;
- /** Associated selection parameter for \ref SEL_SUBEXPRREF elements. */
- struct gmx_ana_selparam_t *param;
- } u;
- /** Memory pool to use for values, or NULL if standard memory handling. */
- struct gmx_sel_mempool_t *mempool;
- /** Internal data for the selection compiler. */
- struct t_compiler_data *cdata;
-
- /*! \brief The first child element.
- *
- * Other children can be accessed through the \p next field of \p child.
- */
- struct t_selelem *child;
- /** The next sibling element. */
- struct t_selelem *next;
- /*! \brief Number of references to this element.
- *
- * Should be larger than one only for \ref SEL_SUBEXPR elements.
- */
- int refcount;
-} t_selelem;
-
-/* In evaluate.c */
-/** Writes out a human-readable name for an evaluation function. */
-extern void
-_gmx_sel_print_evalfunc_name(FILE *fp, sel_evalfunc evalfunc);
-
-/** Allocates memory and performs some common initialization for a \c t_selelem. */
-extern t_selelem *
-_gmx_selelem_create(e_selelem_t type);
-/** Sets the value type of a \c t_selelem. */
-extern int
-_gmx_selelem_set_vtype(t_selelem *sel, e_selvalue_t vtype);
-/** Reserves memory for value of a \c t_selelem from a memory pool. */
-extern int
-_gmx_selelem_mempool_reserve(t_selelem *sel, int count);
-/** Releases memory pool used for value of a \c t_selelem. */
-extern void
-_gmx_selelem_mempool_release(t_selelem *sel);
-/** Frees the memory allocated for a \c t_selelem structure and all its children. */
-extern void
-_gmx_selelem_free(t_selelem *sel);
-/** Frees the memory allocated for a \c t_selelem structure, all its children, and also all structures referenced through t_selelem::next fields. */
-extern void
-_gmx_selelem_free_chain(t_selelem *first);
-
-/** Frees the memory allocated for the \c t_selelem::d union. */
-extern void
-_gmx_selelem_free_values(t_selelem *sel);
-/** Frees the memory allocated for a selection method. */
-extern void
-_gmx_selelem_free_method(struct gmx_ana_selmethod_t *method, void *mdata);
-/** Frees the memory allocated for the \c t_selelem::u field. */
-extern void
-_gmx_selelem_free_exprdata(t_selelem *sel);
-/* In compiler.c */
-/** Frees the memory allocated for the selection compiler. */
-extern void
-_gmx_selelem_free_compiler_data(t_selelem *sel);
-
-/** Prints a human-readable version of a selection element subtree. */
-extern void
-_gmx_selelem_print_tree(FILE *fp, t_selelem *root, gmx_bool bValues, int level);
-/* In compile.c */
-/** Prints a human-readable version of the internal compiler data structure. */
-extern void
-_gmx_selelem_print_compiler_info(FILE *fp, t_selelem *sel, int level);
-
-/** Returns TRUE if the selection element subtree requires topology information for evaluation. */
-extern gmx_bool
-_gmx_selelem_requires_top(t_selelem *root);
-
-/* In sm_insolidangle.c */
-/** Returns TRUE if the covered fraction of the selection can be calculated. */
-extern gmx_bool
-_gmx_selelem_can_estimate_cover(t_selelem *sel);
-/** Returns the covered fraction of the selection for the current frame. */
-extern real
-_gmx_selelem_estimate_coverfrac(t_selelem *sel);
-
-/*@}*/
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in selhelp.c.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <macros.h>
-#include <string2.h>
-#include <wman.h>
-
-#include "selcollection.h"
-#include "selmethod.h"
-#include "selhelp.h"
-#include "symrec.h"
-
-typedef struct {
- const char *topic;
- int nl;
- const char **text;
-} t_selection_help_item;
-
-static const char *help_common[] = {
- "SELECTION HELP[PAR]",
-
- "This program supports selections in addition to traditional index files.",
- "Please read the subtopic pages (available through \"help topic\") for",
- "more information.",
- "Explanation of command-line arguments for specifying selections can be",
- "found under the \"cmdline\" subtopic, and general selection syntax is",
- "described under \"syntax\". Available keywords can be found under",
- "\"keywords\", and concrete examples under \"examples\".",
- "Other subtopics give more details on certain aspects.",
- "\"help all\" prints the help for all subtopics.",
-};
-
-static const char *help_arithmetic[] = {
- "ARITHMETIC EXPRESSIONS IN SELECTIONS[PAR]",
-
- "Basic arithmetic evaluation is supported for numeric expressions.",
- "Supported operations are addition, subtraction, negation, multiplication,",
- "division, and exponentiation (using ^).",
- "Result of a division by zero or other illegal operations is undefined.",
-};
-
-static const char *help_cmdline[] = {
- "SELECTION COMMAND-LINE ARGUMENTS[PAR]",
-
- "There are two alternative command-line arguments for specifying",
- "selections:[BR]",
- "1. [TT]-select[tt] can be used to specify the complete selection as a",
- "string on the command line.[BR]",
- "2. [TT]-sf[tt] can be used to specify a file name from which the",
- "selection is read.[BR]",
- "If both options are specified, [TT]-select[tt] takes precedence.",
- "If neither of the above is present, the user is prompted to type the",
- "selection on the standard input (a pipe can also be used to provide",
- "the selections in this case).",
- "This is also done if an empty string is passed to [TT]-select[tt].[PAR]",
-
- "Option [TT]-n[tt] can be used to provide an index file.",
- "If no index file is provided, default groups are generated.",
- "In both cases, the user can also select an index group instead of",
- "writing a full selection.",
- "The default groups are generated by reading selections from a file",
- "[TT]defselection.dat[tt]. If such a file is found in the current",
- "directory, it is used instead of the one provided by default.[PAR]",
-
- "Depending on the tool, two additional command-line arguments may be",
- "available to control the behavior:[BR]",
- "1. [TT]-seltype[tt] can be used to specify the default type of",
- "positions to calculate for each selection.[BR]",
- "2. [TT]-selrpos[tt] can be used to specify the default type of",
- "positions used in selecting atoms by coordinates.[BR]",
- "See \"help positions\" for more information on these options.",
-};
-
-static const char *help_eval[] = {
- "SELECTION EVALUATION AND OPTIMIZATION[PAR]",
-
- "Boolean evaluation proceeds from left to right and is short-circuiting",
- "i.e., as soon as it is known whether an atom will be selected, the",
- "remaining expressions are not evaluated at all.",
- "This can be used to optimize the selections: you should write the",
- "most restrictive and/or the most inexpensive expressions first in",
- "boolean expressions.",
- "The relative ordering between dynamic and static expressions does not",
- "matter: all static expressions are evaluated only once, before the first",
- "frame, and the result becomes the leftmost expression.[PAR]",
-
- "Another point for optimization is in common subexpressions: they are not",
- "automatically recognized, but can be manually optimized by the use of",
- "variables. This can have a big impact on the performance of complex",
- "selections, in particular if you define several index groups like this:",
- " [TT]rdist = distance from com of resnr 1 to 5;[tt][BR]",
- " [TT]resname RES and rdist < 2;[tt][BR]",
- " [TT]resname RES and rdist < 4;[tt][BR]",
- " [TT]resname RES and rdist < 6;[tt][BR]",
- "Without the variable assignment, the distances would be evaluated three",
- "times, although they are exactly the same within each selection.",
- "Anything assigned into a variable becomes a common subexpression that",
- "is evaluated only once during a frame.",
- "Currently, in some cases the use of variables can actually lead to a small",
- "performance loss because of the checks necessary to determine for which",
- "atoms the expression has already been evaluated, but this should not be",
- "a major problem.",
-};
-
-static const char *help_examples[] = {
- "SELECTION EXAMPLES[PAR]",
-
- "Below, examples of increasingly complex selections are given.[PAR]",
-
- "Selection of all water oxygens:[BR]",
- " resname SOL and name OW",
- "[PAR]",
-
- "Centers of mass of residues 1 to 5 and 10:[BR]",
- " res_com of resnr 1 to 5 10",
- "[PAR]",
-
- "All atoms farther than 1 nm of a fixed position:[BR]",
- " not within 1 of (1.2, 3.1, 2.4)",
- "[PAR]",
-
- "All atoms of a residue LIG within 0.5 nm of a protein (with a custom name):[BR]",
- " \"Close to protein\" resname LIG and within 0.5 of group \"Protein\"",
- "[PAR]",
-
- "All protein residues that have at least one atom within 0.5 nm of a residue LIG:[BR]",
- " group \"Protein\" and same residue as within 0.5 of resname LIG",
- "[PAR]",
-
- "All RES residues whose COM is between 2 and 4 nm from the COM of all of them:[BR]",
- " rdist = res_com distance from com of resname RES[BR]",
- " resname RES and rdist >= 2 and rdist <= 4",
- "[PAR]",
-
- "Selection like C1 C2 C2 C3 C3 C4 ... C8 C9 (e.g., for g_bond):[BR]",
- " name \"C[1-8]\" merge name \"C[2-9]\"",
-};
-
-static const char *help_keywords[] = {
- "SELECTION KEYWORDS[PAR]",
-
- "The following selection keywords are currently available.",
- "For keywords marked with a star, additional help is available through",
- "\"help KEYWORD\", where KEYWORD is the name of the keyword.",
-};
-
-static const char *help_limits[] = {
- "SELECTION LIMITATIONS[PAR]",
-
- "Some analysis programs may require a special structure for the input",
- "selections (e.g., [TT]g_angle[tt] requires the index group to be made",
- "of groups of three or four atoms).",
- "For such programs, it is up to the user to provide a proper selection",
- "expression that always returns such positions.",
- "[PAR]",
-
- "Due to technical reasons, having a negative value as the first value in",
- "expressions like[BR]",
- "[TT]charge -1 to -0.7[tt][BR]",
- "result in a syntax error. A workaround is to write[BR]",
- "[TT]charge {-1 to -0.7}[tt][BR]",
- "instead.",
-};
-
-static const char *help_positions[] = {
- "SPECIFYING POSITIONS[PAR]",
-
- "Possible ways of specifying positions in selections are:[PAR]",
-
- "1. A constant position can be defined as [TT][XX, YY, ZZ][tt], where",
- "[TT]XX[tt], [TT]YY[tt] and [TT]ZZ[tt] are real numbers.[PAR]",
-
- "2. [TT]com of ATOM_EXPR [pbc][tt] or [TT]cog of ATOM_EXPR [pbc][tt]",
- "calculate the center of mass/geometry of [TT]ATOM_EXPR[tt]. If",
- "[TT]pbc[tt] is specified, the center is calculated iteratively to try",
- "to deal with cases where [TT]ATOM_EXPR[tt] wraps around periodic",
- "boundary conditions.[PAR]",
-
- "3. [TT]POSTYPE of ATOM_EXPR[tt] calculates the specified positions for",
- "the atoms in [TT]ATOM_EXPR[tt].",
- "[TT]POSTYPE[tt] can be [TT]atom[tt], [TT]res_com[tt], [TT]res_cog[tt],",
- "[TT]mol_com[tt] or [TT]mol_cog[tt], with an optional prefix [TT]whole_[tt]",
- "[TT]part_[tt] or [TT]dyn_[tt].",
- "[TT]whole_[tt] calculates the centers for the whole residue/molecule,",
- "even if only part of it is selected.",
- "[TT]part_[tt] prefix calculates the centers for the selected atoms, but",
- "uses always the same atoms for the same residue/molecule. The used atoms",
- "are determined from the the largest group allowed by the selection.",
- "[TT]dyn_[tt] calculates the centers strictly only for the selected atoms.",
- "If no prefix is specified, whole selections default to [TT]part_[tt] and",
- "other places default to [TT]whole_[tt].",
- "The latter is often desirable to select the same molecules in different",
- "tools, while the first is a compromise between speed ([TT]dyn_[tt]",
- "positions can be slower to evaluate than [TT]part_[tt]) and intuitive",
- "behavior.[PAR]",
-
- "4. [TT]ATOM_EXPR[tt], when given for whole selections, is handled as 3.",
- "above, using the position type from the command-line argument",
- "[TT]-seltype[tt].[PAR]",
-
- "Selection keywords that select atoms based on their positions, such as",
- "[TT]dist from[tt], use by default the positions defined by the",
- "[TT]-selrpos[tt] command-line option.",
- "This can be overridden by prepending a [TT]POSTYPE[tt] specifier to the",
- "keyword. For example, [TT]res_com dist from POS[tt] evaluates the",
- "residue center of mass distances. In the example, all atoms of a residue",
- "are either selected or not, based on the single distance calculated.",
-};
-
-static const char *help_syntax[] = {
- "SELECTION SYNTAX[PAR]",
-
- "A set of selections consists of one or more selections, separated by",
- "semicolons. Each selection defines a set of positions for the analysis.",
- "Each selection can also be preceded by a string that gives a name for",
- "the selection for use in, e.g., graph legends.",
- "If no name is provided, the string used for the selection is used",
- "automatically as the name.[PAR]",
-
- "For interactive input, the syntax is slightly altered: line breaks can",
- "also be used to separate selections. \\ followed by a line break can",
- "be used to continue a line if necessary.",
- "Notice that the above only applies to real interactive input,",
- "not if you provide the selections, e.g., from a pipe.[PAR]",
-
- "It is possible to use variables to store selection expressions.",
- "A variable is defined with the following syntax:[BR]",
- "[TT]VARNAME = EXPR ;[tt][BR]",
- "where [TT]EXPR[tt] is any valid selection expression.",
- "After this, [TT]VARNAME[tt] can be used anywhere where [TT]EXPR[tt]",
- "would be valid.[PAR]",
-
- "Selections are composed of three main types of expressions, those that",
- "define atoms ([TT]ATOM_EXPR[tt]s), those that define positions",
- "([TT]POS_EXPR[tt]s), and those that evaluate to numeric values",
- "([TT]NUM_EXPR[tt]s). Each selection should be a [TT]POS_EXPR[tt]",
- "or a [TT]ATOM_EXPR[tt] (the latter is automatically converted to",
- "positions). The basic rules are as follows:[BR]",
- "1. An expression like [TT]NUM_EXPR1 < NUM_EXPR2[tt] evaluates to an",
- "[TT]ATOM_EXPR[tt] that selects all the atoms for which the comparison",
- "is true.[BR]",
- "2. Atom expressions can be combined with gmx_boolean operations such as",
- "[TT]not ATOM_EXPR[tt], [TT]ATOM_EXPR and ATOM_EXPR[tt], or",
- "[TT]ATOM_EXPR or ATOM_EXPR[tt]. Parentheses can be used to alter the",
- "evaluation order.[BR]",
- "3. [TT]ATOM_EXPR[tt] expressions can be converted into [TT]POS_EXPR[tt]",
- "expressions in various ways, see \"help positions\" for more details.[PAR]",
-
- "Some keywords select atoms based on string values such as the atom name.",
- "For these keywords, it is possible to use wildcards ([TT]name \"C*\"[tt])",
- "or regular expressions (e.g., [TT]resname \"R[AB]\"[tt]).",
- "The match type is automatically guessed from the string: if it contains",
- "other characters than letters, numbers, '*', or '?', it is interpreted",
- "as a regular expression.",
- "Strings that contain non-alphanumeric characters should be enclosed in",
- "double quotes as in the examples. For other strings, the quotes are",
- "optional, but if the value conflicts with a reserved keyword, a syntax",
- "error will occur. If your strings contain uppercase letters, this should",
- "not happen.[PAR]",
-
- "Index groups provided with the [TT]-n[tt] command-line option or",
- "generated by default can be accessed with [TT]group NR[tt] or",
- "[TT]group NAME[tt], where [TT]NR[tt] is a zero-based index of the group",
- "and [TT]NAME[tt] is part of the name of the desired group.",
- "The keyword [TT]group[tt] is optional if the whole selection is",
- "provided from an index group.",
- "To see a list of available groups in the interactive mode, press enter",
- "in the beginning of a line.",
-};
-
-static const t_selection_help_item helpitems[] = {
- {NULL, asize(help_common), help_common},
- {"cmdline", asize(help_cmdline), help_cmdline},
- {"syntax", asize(help_syntax), help_syntax},
- {"positions", asize(help_positions), help_positions},
- {"arithmetic", asize(help_arithmetic), help_arithmetic},
- {"keywords", asize(help_keywords), help_keywords},
- {"evaluation", asize(help_eval), help_eval},
- {"limitations", asize(help_limits), help_limits},
- {"examples", asize(help_examples), help_examples},
-};
-
-/*! \brief
- * Prints a brief list of keywords (selection methods) available.
- *
- * \param[in] sc Selection collection for which the list should be printed.
- * \param[in] type Only methods that return this type are printed.
- * \param[in] bMod If FALSE, \ref SMETH_MODIFIER methods are excluded, otherwise
- * only them are printed.
- */
-static void
-print_keyword_list(struct gmx_ana_selcollection_t *sc, e_selvalue_t type,
- gmx_bool bMod)
-{
- gmx_sel_symrec_t *symbol;
-
- symbol = _gmx_sel_first_symbol(sc->symtab, SYMBOL_METHOD);
- while (symbol)
- {
- gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol);
- gmx_bool bShow;
- bShow = (method->type == type)
- && ((bMod && (method->flags & SMETH_MODIFIER))
- || (!bMod && !(method->flags & SMETH_MODIFIER)));
- if (bShow)
- {
- fprintf(stderr, " %c ",
- (method->help.nlhelp > 0 && method->help.help) ? '*' : ' ');
- if (method->help.syntax)
- {
- fprintf(stderr, "%s\n", method->help.syntax);
- }
- else
- {
- const char *symname = _gmx_sel_sym_name(symbol);
-
- fprintf(stderr, "%s", symname);
- if (strcmp(symname, method->name) != 0)
- {
- fprintf(stderr, " (synonym for %s)", method->name);
- }
- fprintf(stderr, "\n");
- }
- }
- symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD);
- }
-}
-
-/*!
- * \param[in] sc Selection collection for which help should be printed.
- * \param[in] topic Topic to print help on, or NULL for general help.
- *
- * \p sc is used to get information on which keywords are available in the
- * present context.
- */
-void
-_gmx_sel_print_help(struct gmx_ana_selcollection_t *sc, const char *topic)
-{
- const t_selection_help_item *item = NULL;
- size_t i;
-
- /* Find the item for the topic */
- if (!topic)
- {
- item = &helpitems[0];
- }
- else if (strcmp(topic, "all") == 0)
- {
- for (i = 0; i < asize(helpitems); ++i)
- {
- item = &helpitems[i];
- _gmx_sel_print_help(sc, item->topic);
- if (i != asize(helpitems) - 1)
- {
- fprintf(stderr, "\n\n");
- }
- }
- return;
- }
- else
- {
- for (i = 1; i < asize(helpitems); ++i)
- {
- if (strncmp(helpitems[i].topic, topic, strlen(topic)) == 0)
- {
- item = &helpitems[i];
- break;
- }
- }
- }
- /* If the topic is not found, check the available methods.
- * If they don't provide any help either, tell the user and exit. */
- if (!item)
- {
- gmx_sel_symrec_t *symbol;
-
- symbol = _gmx_sel_first_symbol(sc->symtab, SYMBOL_METHOD);
- while (symbol)
- {
- gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol);
- if (method->help.nlhelp > 0 && method->help.help
- && strncmp(method->name, topic, strlen(topic)) == 0)
- {
- print_tty_formatted(stderr, method->help.nlhelp,
- method->help.help, 0, NULL, NULL, FALSE);
- return;
- }
- symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD);
- }
-
- fprintf(stderr, "No help available for '%s'.\n", topic);
- return;
- }
- /* Print the help */
- print_tty_formatted(stderr, item->nl, item->text, 0, NULL, NULL, FALSE);
- /* Special handling of certain pages */
- if (!topic)
- {
- int len = 0;
-
- /* Print the subtopics on the main page */
- fprintf(stderr, "\nAvailable subtopics:\n");
- for (i = 1; i < asize(helpitems); ++i)
- {
- int len1 = strlen(helpitems[i].topic) + 2;
-
- len += len1;
- if (len > 79)
- {
- fprintf(stderr, "\n");
- len = len1;
- }
- fprintf(stderr, " %s", helpitems[i].topic);
- }
- fprintf(stderr, "\n");
- }
- else if (strcmp(item->topic, "keywords") == 0)
- {
- /* Print the list of keywords */
- fprintf(stderr, "\nKeywords that select atoms by an integer property:\n");
- fprintf(stderr, "(use in expressions or like \"atomnr 1 to 5 7 9\")\n");
- print_keyword_list(sc, INT_VALUE, FALSE);
-
- fprintf(stderr, "\nKeywords that select atoms by a numeric property:\n");
- fprintf(stderr, "(use in expressions or like \"occupancy 0.5 to 1\")\n");
- print_keyword_list(sc, REAL_VALUE, FALSE);
-
- fprintf(stderr, "\nKeywords that select atoms by a string property:\n");
- fprintf(stderr, "(use like \"name PATTERN [PATTERN] ...\")\n");
- print_keyword_list(sc, STR_VALUE, FALSE);
-
- fprintf(stderr, "\nAdditional keywords that directly select atoms:\n");
- print_keyword_list(sc, GROUP_VALUE, FALSE);
-
- fprintf(stderr, "\nKeywords that directly evaluate to positions:\n");
- fprintf(stderr, "(see also \"help positions\")\n");
- print_keyword_list(sc, POS_VALUE, FALSE);
-
- fprintf(stderr, "\nAdditional keywords:\n");
- print_keyword_list(sc, POS_VALUE, TRUE);
- print_keyword_list(sc, NO_VALUE, TRUE);
- }
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Functions for printing help for selections.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_HELP_H
-#define SELECTION_HELP_H
-
-struct gmx_ana_selcollection_t;
-
-/** Prints help for writing selections. */
-void
-_gmx_sel_print_help(struct gmx_ana_selcollection_t *sc, const char *topic);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in selmethod.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#include <stdarg.h>
-
-#include <macros.h>
-#include <string2.h>
-
-#include <selmethod.h>
-
-#include "selcollection.h"
-#include "symrec.h"
-
-/*
- * These global variables cannot be const because gmx_ana_selmethod_register()
- * modifies them to set some defaults. This is a small price to pay for the
- * convenience of not having to remember exactly how the selection compiler
- * expects the structures to be filled, and even more so if the expectations
- * change. Also, even if the gmx_ana_selmethod_t structures were made const,
- * the parameters could not be without typecasts somewhere, because the param
- * field in gmx_ana_selmethod_t cannot be declared const.
- *
- * Even though the variables may be modified, this should be thread-safe as
- * modifications are done only in gmx_ana_selmethod_register(), and it should
- * work even if called more than once for the same structure, and even if
- * called concurrently from multiple threads (as long as the selection
- * collection is not the same).
- *
- * All of these problems should go away if/when the selection methods are
- * implemented as C++ classes.
- */
-
-/* From sm_com.c */
-extern gmx_ana_selmethod_t sm_cog;
-extern gmx_ana_selmethod_t sm_com;
-/* From sm_simple.c */
-extern gmx_ana_selmethod_t sm_all;
-extern gmx_ana_selmethod_t sm_none;
-extern gmx_ana_selmethod_t sm_atomnr;
-extern gmx_ana_selmethod_t sm_resnr;
-extern gmx_ana_selmethod_t sm_resindex;
-extern gmx_ana_selmethod_t sm_molindex;
-extern gmx_ana_selmethod_t sm_atomname;
-extern gmx_ana_selmethod_t sm_atomtype;
-extern gmx_ana_selmethod_t sm_resname;
-extern gmx_ana_selmethod_t sm_insertcode;
-extern gmx_ana_selmethod_t sm_chain;
-extern gmx_ana_selmethod_t sm_mass;
-extern gmx_ana_selmethod_t sm_charge;
-extern gmx_ana_selmethod_t sm_altloc;
-extern gmx_ana_selmethod_t sm_occupancy;
-extern gmx_ana_selmethod_t sm_betafactor;
-extern gmx_ana_selmethod_t sm_x;
-extern gmx_ana_selmethod_t sm_y;
-extern gmx_ana_selmethod_t sm_z;
-/* From sm_distance.c */
-extern gmx_ana_selmethod_t sm_distance;
-extern gmx_ana_selmethod_t sm_mindistance;
-extern gmx_ana_selmethod_t sm_within;
-/* From sm_insolidangle.c */
-extern gmx_ana_selmethod_t sm_insolidangle;
-/* From sm_same.c */
-extern gmx_ana_selmethod_t sm_same;
-
-/* From sm_merge.c */
-extern gmx_ana_selmethod_t sm_merge;
-extern gmx_ana_selmethod_t sm_plus;
-/* From sm_permute.c */
-extern gmx_ana_selmethod_t sm_permute;
-
-/*! \brief
- * Helper structure for defining selection methods.
- */
-typedef struct {
- /*! \brief
- * Name to register the method under.
- *
- * If NULL, use the actual name of the method.
- * This field is used for defining synonyms.
- */
- const char *name;
- /** Method data structure to register. */
- gmx_ana_selmethod_t *method;
-} t_register_method;
-
-/** Array of selection methods defined in the library. */
-static const t_register_method smtable_def[] = {
- {NULL, &sm_cog},
- {NULL, &sm_com},
-
- {NULL, &sm_all},
- {NULL, &sm_none},
- {NULL, &sm_atomnr},
- {NULL, &sm_resnr},
- {"resid", &sm_resnr},
- {NULL, &sm_resindex},
- {"residue", &sm_resindex},
- {NULL, &sm_molindex},
- {"mol", &sm_molindex},
- {"molecule", &sm_molindex},
- {NULL, &sm_atomname},
- {NULL, &sm_atomtype},
- {NULL, &sm_resname},
- {NULL, &sm_insertcode},
- {NULL, &sm_chain},
- {NULL, &sm_mass},
- {NULL, &sm_charge},
- {NULL, &sm_altloc},
- {NULL, &sm_occupancy},
- {NULL, &sm_betafactor},
- {NULL, &sm_x},
- {NULL, &sm_y},
- {NULL, &sm_z},
-
- {NULL, &sm_distance},
- {NULL, &sm_mindistance},
- {NULL, &sm_within},
- {NULL, &sm_insolidangle},
- {NULL, &sm_same},
-
- {NULL, &sm_merge},
- {NULL, &sm_plus},
- {NULL, &sm_permute},
-};
-
-/*! \brief
- * Convenience function for reporting errors found in selection methods.
- */
-static void
-report_error(FILE *fp, const char *name, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- if (fp)
- {
- fprintf(fp, "selection method '%s': ", name);
- vfprintf(fp, fmt, ap);
- fprintf(fp, "\n");
- }
- va_end(ap);
-}
-
-/*! \brief
- * Convenience function for reporting errors found in selection method parameters.
- */
-static void
-report_param_error(FILE *fp, const char *mname, const char *pname,
- const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- if (fp)
- {
- fprintf(fp, "selection method '%s': parameter '%s': ", mname, pname);
- vfprintf(fp, fmt, ap);
- fprintf(fp, "\n");
- }
- va_end(ap);
-}
-
-/*! \brief
- * Checks the validity of parameters.
- *
- * \param[in] fp File handle to use for diagnostic messages
- * (can be NULL).
- * \param[in] name Name of the method (used for error messages).
- * \param[in] nparams Number of parameters in \p param.
- * \param[in,out] param Parameter array
- * (only the \c flags field of gmx_boolean parameters may be modified).
- * \param[in] symtab Symbol table (used for checking overlaps).
- * \returns TRUE if there are no problems with the parameters,
- * FALSE otherwise.
- *
- * This function performs some checks common to both check_method() and
- * check_modifier().
- * The purpose of these checks is to ensure that the selection parser does not
- * need to check for the validity of the parameters at each turn, and to
- * report programming errors as early as possible.
- * If you remove a check, make sure that the parameter parser can handle the
- * resulting parameters.
- */
-static gmx_bool
-check_params(FILE *fp, const char *name, int nparams, gmx_ana_selparam_t param[],
- gmx_sel_symtab_t *symtab)
-{
- gmx_bool bOk = TRUE;
- gmx_sel_symrec_t *sym;
- int i, j;
-
- if (nparams > 0 && !param)
- {
- report_error(fp, name, "error: missing parameter data");
- bOk = FALSE;
- return FALSE;
- }
- if (nparams == 0 && param)
- {
- report_error(fp, name, "warning: parameter data unused because nparams=0");
- }
- /* Check each parameter */
- for (i = 0; i < nparams; ++i)
- {
- /* Check that there is at most one NULL name, in the beginning */
- if (param[i].name == NULL && i > 0)
- {
- report_error(fp, name, "error: NULL parameter should be the first one");
- bOk = FALSE;
- continue;
- }
- /* Check for duplicates */
- for (j = 0; j < i; ++j)
- {
- if (param[j].name == NULL)
- {
- continue;
- }
- if (!gmx_strcasecmp(param[i].name, param[j].name))
- {
- report_error(fp, name, "error: duplicate parameter name '%s'", param[i].name);
- bOk = FALSE;
- break;
- }
- }
- /* Check flags */
- if (param[i].flags & SPAR_SET)
- {
- report_param_error(fp, name, param[i].name, "warning: flag SPAR_SET is set");
- param[i].flags &= ~SPAR_SET;
- }
- if (param[i].flags & SPAR_RANGES)
- {
- if (param[i].val.type != INT_VALUE && param[i].val.type != REAL_VALUE)
- {
- report_param_error(fp, name, param[i].name, "error: SPAR_RANGES cannot be set for a non-numeric parameter");
- bOk = FALSE;
- }
- if (param[i].flags & SPAR_DYNAMIC)
- {
- report_param_error(fp, name, param[i].name, "warning: SPAR_DYNAMIC does not have effect with SPAR_RANGES");
- param[i].flags &= ~SPAR_DYNAMIC;
- }
- if (!(param[i].flags & SPAR_VARNUM) && param[i].val.nr != 1)
- {
- report_param_error(fp, name, param[i].name, "error: range should take either one or an arbitrary number of values");
- bOk = FALSE;
- }
- if (param[i].flags & SPAR_ATOMVAL)
- {
- report_param_error(fp, name, param[i].name, "error: SPAR_RANGES and SPAR_ATOMVAL both set");
- bOk = FALSE;
- }
- }
- if ((param[i].flags & SPAR_VARNUM) && (param[i].flags & SPAR_ATOMVAL))
- {
- report_param_error(fp, name, param[i].name, "error: SPAR_VARNUM and SPAR_ATOMVAL both set");
- bOk = FALSE;
- }
- if (param[i].flags & SPAR_ENUMVAL)
- {
- if (param[i].val.type != STR_VALUE)
- {
- report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL can only be set for string parameters");
- bOk = FALSE;
- }
- if (param[i].val.nr != 1)
- {
- report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL parameters should take exactly one value");
- bOk = FALSE;
- }
- if (param[i].flags & (SPAR_DYNAMIC | SPAR_VARNUM | SPAR_ATOMVAL))
- {
- report_param_error(fp, name, param[i].name, "error: only SPAR_OPTIONAL supported with SPAR_ENUMVAL");
- bOk = FALSE;
- }
- }
- /* Check gmx_boolean parameters */
- if (param[i].val.type == NO_VALUE)
- {
- if (param[i].val.nr != 0)
- {
- report_param_error(fp, name, param[i].name, "error: number of values should be zero for gmx_boolean parameters");
- bOk = FALSE;
- }
- /* The gmx_boolean parameters should always be optional, so set the
- * flag for convenience. */
- param[i].flags |= SPAR_OPTIONAL;
- /* Any other flags should not be specified */
- if (param[i].flags & ~SPAR_OPTIONAL)
- {
- report_param_error(fp, name, param[i].name, "error: gmx_boolean parameter should not have any flags set");
- bOk = FALSE;
- }
- }
- /* Check val.nr */
- if (param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL))
- {
- if (param[i].val.nr != -1)
- {
- report_param_error(fp, name, param[i].name, "warning: val.nr is not -1 although SPAR_VARNUM/SPAR_ATOMVAL is set");
- }
- param[i].val.nr = -1;
- }
- else if (param[i].val.type != NO_VALUE)
- {
- if (param[i].val.nr <= 0)
- {
- report_param_error(fp, name, param[i].name, "error: val.nr <= 0");
- bOk = FALSE;
- }
- }
- /* Check that the value pointer is NULL */
- if (param[i].nvalptr != NULL)
- {
- report_param_error(fp, name, param[i].name, "warning: nvalptr is set");
- }
- if (param[i].val.u.ptr != NULL && !(param[i].flags & SPAR_ENUMVAL))
- {
- report_param_error(fp, name, param[i].name, "warning: value pointer is set");
- }
- /* Check that the name contains only valid characters */
- if (param[i].name == NULL)
- {
- continue;
- }
- if (!isalpha(param[i].name[0]))
- {
- report_param_error(fp, name, param[i].name, "error: name does not begin with a letter");
- bOk = FALSE;
- continue;
- }
- for (j = 1; param[i].name[j] != 0; ++j)
- {
- if (param[i].name[j] != '_' && !isalnum(param[i].name[j]))
- {
- report_param_error(fp, name, param[i].name, "error: name contains non-alphanumeric characters");
- bOk = FALSE;
- break;
- }
- }
- if (param[i].name[j] != 0)
- {
- continue;
- }
- /* Check that the name does not conflict with a method */
- if (_gmx_sel_find_symbol(symtab, param[i].name, TRUE))
- {
- report_param_error(fp, name, param[i].name, "error: name conflicts with another method or a keyword");
- bOk = FALSE;
- }
- } /* End of parameter loop */
- /* Check parameters of existing methods */
- sym = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD);
- while (sym)
- {
- gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(sym);
- gmx_ana_selparam_t *param =
- gmx_ana_selmethod_find_param(name, method);
- if (param)
- {
- report_param_error(fp, method->name, param->name, "error: name conflicts with another method or a keyword");
- bOk = FALSE;
- }
- sym = _gmx_sel_next_symbol(sym, SYMBOL_METHOD);
- }
- return bOk;
-}
-
-/*! \brief
- * Checks the validity of selection method callback functions.
- *
- * \param[in] fp File handle to use for diagnostic messages
- * (can be NULL).
- * \param[in] method The method to check.
- * \returns TRUE if there are no problems, FALSE otherwise.
- *
- * This function performs some checks common to both check_method() and
- * check_modifier().
- * This function checks that all the required callbacks are defined, i.e.,
- * not NULL, to find programming errors.
- */
-static gmx_bool
-check_callbacks(FILE *fp, gmx_ana_selmethod_t *method)
-{
- gmx_bool bOk = TRUE;
- gmx_bool bNeedInit;
- int i;
-
- /* Make some checks on init_data and free */
- if (method->nparams > 0 && !method->init_data)
- {
- report_error(fp, method->name, "error: init_data should be provided because the method has parameters");
- bOk = FALSE;
- }
- if (method->free && !method->init_data)
- {
- report_error(fp, method->name, "warning: free is not used because of missing init_data");
- }
- /* Check presence of outinit for position-valued methods */
- if (method->type == POS_VALUE && !method->outinit)
- {
- report_error(fp, method->name, "error: outinit should be provided because the method has POS_VALUE");
- bOk = FALSE;
- }
- /* Warn of dynamic callbacks in static methods */
- if (!(method->flags & SMETH_MODIFIER))
- {
- if (method->pupdate && !(method->flags & SMETH_DYNAMIC))
- {
- report_error(fp, method->name, "warning: pupdate not used because the method is static");
- method->pupdate = NULL;
- }
- }
- /* Check that there is an evaluation function */
- if (method->type != NO_VALUE && !method->update && !method->pupdate)
- {
- report_error(fp, method->name, "error: evaluation function missing");
- bOk = FALSE;
- }
- /* Loop through the parameters to determine if initialization callbacks
- * are needed. */
- bNeedInit = FALSE;
- for (i = 0; i < method->nparams; ++i)
- {
- if (method->param[i].val.type != POS_VALUE
- && (method->param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
- {
- bNeedInit = TRUE;
- }
- }
- /* Check that the callbacks required by the parameters are present */
- if (bNeedInit && !method->init)
- {
- report_error(fp, method->name, "error: init should be provided");
- bOk = FALSE;
- }
- return bOk;
-}
-
-/*!
- * Checks the validity of a selection method.
- *
- * \param[in] fp File handle to use for diagnostic messages
- * (can be NULL).
- * \param[in,out] method Method to check.
- * \param[in] symtab Symbol table (used for checking overlaps).
- *
- * Checks the validity of the given selection method data structure
- * that does not have \ref SMETH_MODIFIER set.
- * If you remove a check, please make sure that the selection parser,
- * compiler, and evaluation functions can deal with the method.
- */
-static gmx_bool
-check_method(FILE *fp, gmx_ana_selmethod_t *method, gmx_sel_symtab_t *symtab)
-{
- gmx_bool bOk = TRUE;
-
- /* Check the type */
- if (method->type == NO_VALUE)
- {
- report_error(fp, method->name, "error: no value type specified");
- bOk = FALSE;
- }
- if (method->type == STR_VALUE && method->nparams > 0)
- {
- report_error(fp, method->name, "error: evaluates to a string but is not a keyword");
- bOk = FALSE;
- }
- /* Check flags */
- if (method->type == GROUP_VALUE)
- {
- /* Group methods should always have SMETH_SINGLEVAL,
- * so set it for convenience. */
- method->flags |= SMETH_SINGLEVAL;
- /* Check that conflicting flags are not present. */
- if (method->flags & SMETH_VARNUMVAL)
- {
- report_error(fp, method->name, "error: SMETH_VARNUMVAL cannot be set for group-valued methods");
- bOk = FALSE;
- }
- }
- else
- {
- if ((method->flags & SMETH_SINGLEVAL)
- && (method->flags & SMETH_VARNUMVAL))
- {
- report_error(fp, method->name, "error: SMETH_SINGLEVAL and SMETH_VARNUMVAL both set");
- bOk = FALSE;
- }
- }
- if ((method->flags & SMETH_CHARVAL) && method->type != STR_VALUE)
- {
- report_error(fp, method->name, "error: SMETH_CHARVAL can only be specified for STR_VALUE methods");
- bOk = FALSE;
- }
- /* Check the parameters */
- if (!check_params(fp, method->name, method->nparams, method->param, symtab))
- {
- bOk = FALSE;
- }
- /* Check the callback pointers */
- if (!check_callbacks(fp, method))
- {
- bOk = FALSE;
- }
-
- return bOk;
-}
-
-/*!
- * Checks the validity of a selection modifier method.
- *
- * \param[in] fp File handle to use for diagnostic messages
- * (can be NULL).
- * \param[in,out] method Method to check.
- * \param[in] symtab Symbol table (used for checking overlaps).
- *
- * Checks the validity of the given selection method data structure
- * that has \ref SMETH_MODIFIER set.
- * If you remove a check, please make sure that the selection parser,
- * compiler, and evaluation functions can deal with the method.
- */
-static gmx_bool
-check_modifier(FILE *fp, gmx_ana_selmethod_t *method, gmx_sel_symtab_t *symtab)
-{
- gmx_bool bOk = TRUE;
-
- /* Check the type */
- if (method->type != NO_VALUE && method->type != POS_VALUE)
- {
- report_error(fp, method->name, "error: modifier should have type POS_VALUE or NO_VALUE");
- bOk = FALSE;
- }
- /* Check flags */
- if (method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
- {
- report_error(fp, method->name, "error: modifier should not have SMETH_SINGLEVAL or SMETH_VARNUMVAL set");
- bOk = FALSE;
- }
- /* Check the parameters */
- /* The first parameter is skipped */
- if (!check_params(fp, method->name, method->nparams-1, method->param+1, symtab))
- {
- bOk = FALSE;
- }
- /* Check the callback pointers */
- if (!check_callbacks(fp, method))
- {
- bOk = FALSE;
- }
- if (method->update)
- {
- report_error(fp, method->name, "error: modifier should not have update");
- bOk = FALSE;
- }
- if (method->type == POS_VALUE && !method->pupdate)
- {
- report_error(fp, method->name, "error: evaluation function missing");
- bOk = FALSE;
- }
-
- return bOk;
-}
-
-/*!
- * \param[in,out] sc Selection collection to registered the method to.
- * \param[in] name Name under which the method should be registered.
- * \param[in] method Method to register.
- * \returns 0 on success, EINVAL if there was something wrong with the
- * method.
- *
- * \p name does not need to match the name of the method, and the same
- * method can be registered multiple times under different names.
- * If \p name equals some previously registered name,
- * an error message is printed and the method is not registered.
- *
- * The function also performs some sanity checking on the input method,
- * and refuses to register it if there are problems.
- * Some problems only generate warnings.
- * All problems are described to \p stderr.
- */
-int
-gmx_ana_selmethod_register(struct gmx_ana_selcollection_t *sc,
- const char *name, gmx_ana_selmethod_t *method)
-{
- gmx_bool bOk;
-
- /* Check the method */
- if (method->flags & SMETH_MODIFIER)
- {
- bOk = check_modifier(stderr, method, sc->symtab);
- }
- else
- {
- bOk = check_method(stderr, method, sc->symtab);
- }
- /* Try to register the method if everything is ok */
- if (bOk)
- {
- if (!_gmx_sel_add_method_symbol(sc->symtab, name, method))
- {
- bOk = FALSE;
- }
- }
- if (!bOk)
- {
- report_error(stderr, name, "warning: not registered");
- return EINVAL;
- }
- return 0;
-}
-
-/*!
- * \param[in,out] sc Selection collection to registered the methods to.
- * \returns 0 on success, -1 if any of the default methods could not be
- * registered.
- */
-int
-gmx_ana_selmethod_register_defaults(struct gmx_ana_selcollection_t *sc)
-{
- size_t i;
- int rc;
- gmx_bool bOk;
-
- bOk = TRUE;
- for (i = 0; i < asize(smtable_def); ++i)
- {
- gmx_ana_selmethod_t *method = smtable_def[i].method;
-
- if (smtable_def[i].name == NULL)
- {
- rc = gmx_ana_selmethod_register(sc, method->name, method);
- }
- else
- {
- rc = gmx_ana_selmethod_register(sc, smtable_def[i].name, method);
- }
- if (rc != 0)
- {
- bOk = FALSE;
- }
- }
- return bOk ? 0 : -1;
-}
-
-/*!
- * \param[in] name Name of the parameter to search.
- * \param[in] method Method to search for the parameter.
- * \returns Pointer to the parameter in the
- * \ref gmx_ana_selmethod_t::param "method->param" array,
- * or NULL if no parameter with name \p name was found.
- *
- * This is a simple wrapper for gmx_ana_selparam_find().
- */
-gmx_ana_selparam_t *
-gmx_ana_selmethod_find_param(const char *name, gmx_ana_selmethod_t *method)
-{
- return gmx_ana_selparam_find(name, method->nparams, method->param);
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in selvalue.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <smalloc.h>
-
-#include <indexutil.h>
-#include <position.h>
-#include <selvalue.h>
-
-/*!
- * \param[out] val Output structure
- *
- * The type of \p val is not touched.
- * Any contents of \p val are discarded without freeing.
- */
-void
-_gmx_selvalue_clear(gmx_ana_selvalue_t *val)
-{
- val->nr = 0;
- val->u.ptr = NULL;
- val->nalloc = 0;
-}
-
-/*!
- * \param[in,out] val Value structure to allocate.
- * \param[in] n Maximum number of values needed.
- * \returns Zero on success.
- *
- * Reserves memory for the values within \p val to store at least \p n values,
- * of the type specified in the \p val structure.
- *
- * If the type is \ref POS_VALUE or \ref GROUP_VALUE, memory is reserved for
- * the data structures, but no memory is reserved inside these newly allocated
- * data structures.
- * Similarly, for \ref STR_VALUE values, the pointers are set to NULL.
- * For other values, the memory is uninitialized.
- */
-int
-_gmx_selvalue_reserve(gmx_ana_selvalue_t *val, int n)
-{
- int i;
-
- if (val->nalloc == -1)
- {
- return 0;
- }
-
- if (!val->u.ptr || val->nalloc < n)
- {
- switch (val->type)
- {
- case INT_VALUE: srenew(val->u.i, n); break;
- case REAL_VALUE: srenew(val->u.r, n); break;
- case STR_VALUE:
- srenew(val->u.s, n);
- for (i = val->nalloc; i < n; ++i)
- {
- val->u.s[i] = NULL;
- }
- break;
- case POS_VALUE:
- srenew(val->u.p, n);
- for (i = val->nalloc; i < n; ++i)
- {
- gmx_ana_pos_clear(&val->u.p[i]);
- }
- break;
- case GROUP_VALUE:
- srenew(val->u.g, n);
- for (i = val->nalloc; i < n; ++i)
- {
- gmx_ana_index_clear(&val->u.g[i]);
- }
- break;
- case NO_VALUE: break;
- }
- val->nalloc = n;
- }
- return 0;
-}
-
-/*!
- * \param[in,out] val Value structure to allocate.
- * \param[in] ptr Pointer where the values should be stored.
- * \returns Zero on success.
- *
- * Automatic memory management is disabled for \p ptr, unless \p ptr is NULL.
- */
-int
-_gmx_selvalue_setstore(gmx_ana_selvalue_t *val, void *ptr)
-{
- val->u.ptr = ptr;
- val->nalloc = (ptr ? -1 : 0);
- return 0;
-}
-
-/*!
- * \param[in,out] val Value structure to allocate.
- * \param[in] ptr Pointer where the values should be stored.
- * \param[in] nalloc Number of values allocated for \p ptr.
- * \returns Zero on success.
- */
-int
-_gmx_selvalue_setstore_alloc(gmx_ana_selvalue_t *val, void *ptr, int nalloc)
-{
- val->u.ptr = ptr;
- val->nalloc = nalloc;
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief
- * Implementation of internal selection method for comparison expressions.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <maths.h>
-#include <macros.h>
-#include <smalloc.h>
-#include <gmx_fatal.h>
-
-#include <selmethod.h>
-
-/** Defines the comparison operator for comparison expressions. */
-typedef enum
-{
- CMP_INVALID, /**< Indicates an error */
- CMP_LESS, /**< '<' */
- CMP_LEQ, /**< '<=' */
- CMP_GTR, /**< '>' */
- CMP_GEQ, /**< '>=' */
- CMP_EQUAL, /**< '==' */
- CMP_NEQ /**< '!=' */
-} e_comparison_t;
-
-/** The operand has a single value. */
-#define CMP_SINGLEVAL 1
-/** The operand value is dynamic. */
-#define CMP_DYNAMICVAL 2
-/** The value is real. */
-#define CMP_REALVAL 4
-/** The integer array is allocated. */
-#define CMP_ALLOCINT 16
-/** The real array is allocated. */
-#define CMP_ALLOCREAL 32
-
-/*! \internal \brief
- * Data structure for comparison expression operand values.
- */
-typedef struct
-{
- /** Flags that describe the type of the operand. */
- int flags;
- /** (Array of) integer value(s). */
- int *i;
- /** (Array of) real value(s). */
- real *r;
-} t_compare_value;
-
-/*! \internal \brief
- * Data structure for comparison expression evaluation.
- */
-typedef struct
-{
- /** Comparison operator as a string. */
- char *cmpop;
- /** Comparison operator type. */
- e_comparison_t cmpt;
- /** Left value. */
- t_compare_value left;
- /** Right value. */
- t_compare_value right;
-} t_methoddata_compare;
-
-/** Allocates data for comparison expression evaluation. */
-static void *
-init_data_compare(int npar, gmx_ana_selparam_t *param);
-/** Initializes data for comparison expression evaluation. */
-static int
-init_compare(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Frees the memory allocated for comparison expression evaluation. */
-static void
-free_data_compare(void *data);
-/** Evaluates comparison expressions. */
-static int
-evaluate_compare(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-
-/** Parameters for comparison expression evaluation. */
-static gmx_ana_selparam_t smparams_compare[] = {
- {"int1", {INT_VALUE, -1, {NULL}}, NULL,
- SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
- {"real1", {REAL_VALUE, -1, {NULL}}, NULL,
- SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
- {"op", {STR_VALUE, 1, {NULL}}, NULL, 0},
- {"int2", {INT_VALUE, -1, {NULL}}, NULL,
- SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
- {"real2", {REAL_VALUE, -1, {NULL}}, NULL,
- SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
-};
-
-/** \internal Selection method data for comparison expression evaluation. */
-gmx_ana_selmethod_t sm_compare = {
- "cmp", GROUP_VALUE, SMETH_SINGLEVAL,
- asize(smparams_compare), smparams_compare,
- &init_data_compare,
- NULL,
- &init_compare,
- NULL,
- &free_data_compare,
- NULL,
- &evaluate_compare,
- NULL,
- {NULL, 0, NULL},
-};
-
-/*! \brief
- * Returns a \c e_comparison_t value corresponding to an operator.
- *
- * \param[in] str String to process.
- * \returns The comparison type corresponding to the first one or two
- * characters of \p str.
- *
- * \p str can contain any number of characters; only the first two
- * are used.
- * If the beginning of \p str does not match any of the recognized types,
- * \ref CMP_INVALID is returned.
- */
-static e_comparison_t
-comparison_type(char *str)
-{
- switch (str[0])
- {
- case '<': return (str[1] == '=') ? CMP_LEQ : CMP_LESS;
- case '>': return (str[1] == '=') ? CMP_GEQ : CMP_GTR;
- case '=': return (str[1] == '=') ? CMP_EQUAL : CMP_INVALID;
- case '!': return (str[1] == '=') ? CMP_NEQ : CMP_INVALID;
- }
- return CMP_INVALID;
-}
-
-/*! \brief
- * Returns a string corresponding to a \c e_comparison_t value.
- *
- * \param[in] cmpt Comparison type to convert.
- * \returns Pointer to a string that corresponds to \p cmpt.
- *
- * The return value points to a string constant and should not be \p free'd.
- *
- * The function returns NULL if \p cmpt is not one of the valid values.
- */
-static const char *
-comparison_type_str(e_comparison_t cmpt)
-{
- switch (cmpt)
- {
- case CMP_INVALID: return "INVALID"; break;
- case CMP_LESS: return "<"; break;
- case CMP_LEQ: return "<="; break;
- case CMP_GTR: return ">"; break;
- case CMP_GEQ: return ">="; break;
- case CMP_EQUAL: return "=="; break;
- case CMP_NEQ: return "!="; break;
- }
- return NULL;
-}
-
-/*!
- * \param[in] fp File to receive the output.
- * \param[in] data Should point to a \c t_methoddata_compare.
- */
-void
-_gmx_selelem_print_compare_info(FILE *fp, void *data)
-{
- t_methoddata_compare *d = (t_methoddata_compare *)data;
-
- fprintf(fp, " \"");
- /* Print the left value */
- if ((d->left.flags & CMP_SINGLEVAL) && !(d->left.flags & CMP_DYNAMICVAL))
- {
- if (d->left.flags & CMP_REALVAL)
- {
- fprintf(fp, "%f ", d->left.r[0]);
- }
- else
- {
- fprintf(fp, "%d ", d->left.i[0]);
- }
- }
- /* Print the operator */
- if (d->cmpt != CMP_INVALID)
- {
- fprintf(fp, "%s", comparison_type_str(d->cmpt));
- }
- else
- {
- fprintf(fp, "%s", d->cmpop);
- }
- /* Print the right value */
- if ((d->right.flags & CMP_SINGLEVAL) && !(d->right.flags & CMP_DYNAMICVAL))
- {
- if (d->right.flags & CMP_REALVAL)
- {
- fprintf(fp, " %f", d->right.r[0]);
- }
- else
- {
- fprintf(fp, " %d", d->right.i[0]);
- }
- }
- fprintf(fp, "\"");
-}
-
-/*!
- * \param[in] npar Not used (should be 5).
- * \param[in,out] param Method parameters (should point to a copy of
- * \ref smparams_compare).
- * \returns Pointer to the allocated data (\c t_methoddata_compare).
- *
- * Allocates memory for a \c t_methoddata_compare structure.
- */
-static void *
-init_data_compare(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_compare *data;
-
- snew(data, 1);
- param[2].val.u.s = &data->cmpop;
- return data;
-}
-
-/* \brief
- * Reverses a comparison operator.
- *
- * \param[in] type Comparison operator to reverse.
- * \returns The correct comparison operator that equals \p type when the
- * left and right sides are interchanged.
- */
-static e_comparison_t
-reverse_comparison_type(e_comparison_t type)
-{
- switch (type)
- {
- case CMP_LESS: return CMP_GTR;
- case CMP_LEQ: return CMP_GEQ;
- case CMP_GTR: return CMP_LESS;
- case CMP_GEQ: return CMP_LEQ;
- default: break;
- }
- return type;
-}
-
-/*! \brief
- * Initializes the value storage for comparison expression.
- *
- * \param[out] val Value structure to initialize.
- * \param[in] param Parameters to use for initialization.
- * \returns The number of values provided for the value, 0 on error.
- */
-static int
-init_comparison_value(t_compare_value *val, gmx_ana_selparam_t param[2])
-{
- int n;
-
- val->flags = 0;
- if (param[0].flags & SPAR_SET)
- {
- val->flags |= (param[0].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
- val->flags |= !(param[0].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL : 0;
- n = param[0].val.nr;
- val->i = param[0].val.u.i;
- }
- else if (param[1].flags & SPAR_SET)
- {
- val->flags |= (param[1].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
- val->flags |= !(param[1].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL : 0;
- val->flags |= CMP_REALVAL;
- n = param[1].val.nr;
- val->r = param[1].val.u.r;
- }
- else
- {
- n = 0;
- val->i = NULL;
- val->r = NULL;
- }
- return n;
-}
-
-/* \brief
- * Converts an integer value to floating point.
- *
- * \param[in] n Number of values in the \p val->u array.
- * \param[in,out] val Value to convert.
- */
-static void
-convert_int_real(int n, t_compare_value *val)
-{
- int i;
- real *rv;
-
- snew(rv, n);
- for (i = 0; i < n; ++i)
- {
- rv[i] = (real)val->i[i];
- }
- /* Free the previous value if one is present. */
- sfree(val->r);
- val->r = rv;
- val->flags |= CMP_REALVAL | CMP_ALLOCREAL;
-}
-
-/* \brief
- * Converts a floating point value to integer.
- *
- * \param[in] n Number of values in the \p val->u array.
- * \param[in,out] val Value to convert.
- * \param[in] cmpt Comparison operator type.
- * \param[in] bRight TRUE if \p val appears on the right hand size of
- * \p cmpt.
- * \returns 0 on success, EINVAL on error.
- *
- * The values are rounded such that the same comparison operator can be used.
- */
-static int
-convert_real_int(int n, t_compare_value *val, e_comparison_t cmpt, gmx_bool bRight)
-{
- int i;
- int *iv;
-
- if (!bRight)
- {
- cmpt = reverse_comparison_type(cmpt);
- }
- snew(iv, n);
- /* Round according to the comparison type */
- for (i = 0; i < n; ++i)
- {
- switch (cmpt)
- {
- case CMP_LESS:
- case CMP_GEQ:
- iv[i] = (int)ceil(val->r[i]);
- break;
- case CMP_GTR:
- case CMP_LEQ:
- iv[i] = (int)floor(val->r[i]);
- break;
- case CMP_EQUAL:
- case CMP_NEQ:
- fprintf(stderr, "comparing equality an integer expression and a real value\n");
- sfree(iv);
- return EINVAL;
- case CMP_INVALID: /* Should not be reached */
- gmx_bug("internal error");
- sfree(iv);
- return EINVAL;
- }
- }
- /* Free the previous value if one is present. */
- sfree(val->i);
- val->i = iv;
- val->flags &= ~CMP_REALVAL;
- val->flags |= CMP_ALLOCINT;
- return 0;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used (should be 5).
- * \param[in] param Method parameters (should point to \ref smparams_compare).
- * \param[in] data Should point to a \c t_methoddata_compare.
- * \returns 0 if the input data is valid, -1 on error.
- */
-static int
-init_compare(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_compare *d = (t_methoddata_compare *)data;
- int n1, n2;
-
- /* Store the values */
- n1 = init_comparison_value(&d->left, ¶m[0]);
- n2 = init_comparison_value(&d->right, ¶m[3]);
- if (n1 == 0 || n2 == 0)
- {
- gmx_bug("one of the values for comparison missing");
- return -1;
- }
- /* Store the comparison type */
- d->cmpt = comparison_type(d->cmpop);
- if (d->cmpt == CMP_INVALID)
- {
- gmx_bug("invalid comparison type");
- return -1;
- }
- /* Convert the values to the same type */
- if ((d->left.flags & CMP_REALVAL) && !(d->right.flags & CMP_REALVAL))
- {
- if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
- {
- /* Nothing can be done */
- }
- else if (!(d->right.flags & CMP_DYNAMICVAL))
- {
- convert_int_real(n2, &d->right);
- }
- else /* d->left is static */
- {
- if (convert_real_int(n1, &d->left, d->cmpt, FALSE))
- {
- return -1;
- }
- }
- }
- else if (!(d->left.flags & CMP_REALVAL) && (d->right.flags & CMP_REALVAL))
- {
- if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
- {
- /* Reverse the sides to place the integer on the right */
- int flags;
- d->left.r = d->right.r;
- d->right.r = NULL;
- d->right.i = d->left.i;
- d->left.i = NULL;
- flags = d->left.flags;
- d->left.flags = d->right.flags;
- d->right.flags = flags;
- d->cmpt = reverse_comparison_type(d->cmpt);
- }
- else if (!(d->left.flags & CMP_DYNAMICVAL))
- {
- convert_int_real(n1, &d->left);
- }
- else /* d->right is static */
- {
- if (convert_real_int(n2, &d->right, d->cmpt, TRUE))
- {
- return -1;
- }
- }
- }
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \c t_methoddata_compare).
- *
- * Frees the memory allocated for \c t_methoddata_compare.
- */
-static void
-free_data_compare(void *data)
-{
- t_methoddata_compare *d = (t_methoddata_compare *)data;
-
- sfree(d->cmpop);
- if (d->left.flags & CMP_ALLOCINT)
- {
- sfree(d->left.i);
- }
- if (d->left.flags & CMP_ALLOCREAL)
- {
- sfree(d->left.r);
- }
- if (d->right.flags & CMP_ALLOCINT)
- {
- sfree(d->right.i);
- }
- if (d->right.flags & CMP_ALLOCREAL)
- {
- sfree(d->right.r);
- }
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Not used.
- * \param[in] pbc Not used.
- * \param[in] g Evaluation index group.
- * \param[out] out Output data structure (\p out->u.g is used).
- * \param[in] data Should point to a \c t_methoddata_compare.
- * \returns 0 for success.
- */
-static int
-evaluate_compare_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_compare *d = (t_methoddata_compare *)data;
- int i, i1, i2, ig;
- int a, b;
- gmx_bool bAccept;
-
- for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
- {
- a = d->left.i[i1];
- b = d->right.i[i2];
- bAccept = FALSE;
- switch (d->cmpt)
- {
- case CMP_INVALID: break;
- case CMP_LESS: bAccept = a < b; break;
- case CMP_LEQ: bAccept = a <= b; break;
- case CMP_GTR: bAccept = a > b; break;
- case CMP_GEQ: bAccept = a >= b; break;
- case CMP_EQUAL: bAccept = a == b; break;
- case CMP_NEQ: bAccept = a != b; break;
- }
- if (bAccept)
- {
- out->u.g->index[ig++] = g->index[i];
- }
- if (!(d->left.flags & CMP_SINGLEVAL))
- {
- ++i1;
- }
- if (!(d->right.flags & CMP_SINGLEVAL))
- {
- ++i2;
- }
- }
- out->u.g->isize = ig;
- return 0;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Not used.
- * \param[in] pbc Not used.
- * \param[in] g Evaluation index group.
- * \param[out] out Output data structure (\p out->u.g is used).
- * \param[in] data Should point to a \c t_methoddata_compare.
- * \returns 0 for success.
- */
-static int
-evaluate_compare_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_compare *d = (t_methoddata_compare *)data;
- int i, i1, i2, ig;
- real a, b;
- gmx_bool bAccept;
-
- for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
- {
- a = d->left.r[i1];
- b = (d->right.flags & CMP_REALVAL) ? d->right.r[i2] : d->right.i[i2];
- bAccept = FALSE;
- switch (d->cmpt)
- {
- case CMP_INVALID: break;
- case CMP_LESS: bAccept = a < b; break;
- case CMP_LEQ: bAccept = a <= b; break;
- case CMP_GTR: bAccept = a > b; break;
- case CMP_GEQ: bAccept = a >= b; break;
- case CMP_EQUAL: bAccept = gmx_within_tol(a, b, GMX_REAL_EPS); break;
- case CMP_NEQ: bAccept = !gmx_within_tol(a, b, GMX_REAL_EPS); break;
- }
- if (bAccept)
- {
- out->u.g->index[ig++] = g->index[i];
- }
- if (!(d->left.flags & CMP_SINGLEVAL))
- {
- ++i1;
- }
- if (!(d->right.flags & CMP_SINGLEVAL))
- {
- ++i2;
- }
- }
- out->u.g->isize = ig;
- return 0;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Not used.
- * \param[in] pbc Not used.
- * \param[in] g Evaluation index group.
- * \param[out] out Output data structure (\p out->u.g is used).
- * \param[in] data Should point to a \c t_methoddata_compare.
- * \returns 0 for success.
- */
-static int
-evaluate_compare(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_compare *d = (t_methoddata_compare *)data;
-
- if (!((d->left.flags | d->right.flags) & CMP_REALVAL))
- {
- return evaluate_compare_int(top, fr, pbc, g, out, data);
- }
- else
- {
- return evaluate_compare_real(top, fr, pbc, g, out, data);
- }
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of distance-based selection methods.
- *
- * This file implements the \p distance, \p mindistance and \p within
- * selection methods.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <macros.h>
-#include <pbc.h>
-#include <smalloc.h>
-#include <vec.h>
-
-#include <nbsearch.h>
-#include <position.h>
-#include <selmethod.h>
-
-/*! \internal \brief
- * Data structure for distance-based selection method.
- *
- * The same data structure is used by all the distance-based methods.
- */
-typedef struct
-{
- /** Cutoff distance. */
- real cutoff;
- /** Positions of the reference points. */
- gmx_ana_pos_t p;
- /** Neighborhood search data. */
- gmx_ana_nbsearch_t *nb;
-} t_methoddata_distance;
-
-/** Allocates data for distance-based selection methods. */
-static void *
-init_data_common(int npar, gmx_ana_selparam_t *param);
-/** Initializes a distance-based selection method. */
-static int
-init_common(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Frees the data allocated for a distance-based selection method. */
-static void
-free_data_common(void *data);
-/** Initializes the evaluation of a distance-based within selection method for a frame. */
-static int
-init_frame_common(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
-/** Evaluates the \p distance selection method. */
-static int
-evaluate_distance(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p within selection method. */
-static int
-evaluate_within(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
-
-/** Parameters for the \p distance selection method. */
-static gmx_ana_selparam_t smparams_distance[] = {
- {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
- {"from", {POS_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
-};
-
-/** Parameters for the \p mindistance selection method. */
-static gmx_ana_selparam_t smparams_mindistance[] = {
- {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
- {"from", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
-};
-
-/** Parameters for the \p within selection method. */
-static gmx_ana_selparam_t smparams_within[] = {
- {NULL, {REAL_VALUE, 1, {NULL}}, NULL, 0},
- {"of", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
-};
-
-/** Help text for the distance selection methods. */
-static const char *help_distance[] = {
- "DISTANCE-BASED SELECTION KEYWORDS[PAR]",
-
- "[TT]distance from POS [cutoff REAL][tt][BR]",
- "[TT]mindistance from POS_EXPR [cutoff REAL][tt][BR]",
- "[TT]within REAL of POS_EXPR[tt][PAR]",
-
- "[TT]distance[tt] and [TT]mindistance[tt] calculate the distance from the",
- "given position(s), the only difference being in that [TT]distance[tt]",
- "only accepts a single position, while any number of positions can be",
- "given for [TT]mindistance[tt], which then calculates the distance to the",
- "closest position.",
- "[TT]within[tt] directly selects atoms that are within [TT]REAL[tt] of",
- "[TT]POS_EXPR[tt].[PAR]",
-
- "For the first two keywords, it is possible to specify a cutoff to speed",
- "up the evaluation: all distances above the specified cutoff are",
- "returned as equal to the cutoff.",
- "Currently, this does nothing, but in the future, it allows the use of",
- "grid-based neighborhood search techniques.",
-};
-
-/** \internal Selection method data for the \p distance method. */
-gmx_ana_selmethod_t sm_distance = {
- "distance", REAL_VALUE, SMETH_DYNAMIC,
- asize(smparams_distance), smparams_distance,
- &init_data_common,
- NULL,
- &init_common,
- NULL,
- &free_data_common,
- &init_frame_common,
- NULL,
- &evaluate_distance,
- {"distance from POS [cutoff REAL]", asize(help_distance), help_distance},
-};
-
-/** \internal Selection method data for the \p distance method. */
-gmx_ana_selmethod_t sm_mindistance = {
- "mindistance", REAL_VALUE, SMETH_DYNAMIC,
- asize(smparams_mindistance), smparams_mindistance,
- &init_data_common,
- NULL,
- &init_common,
- NULL,
- &free_data_common,
- &init_frame_common,
- NULL,
- &evaluate_distance,
- {"mindistance from POS_EXPR [cutoff REAL]", asize(help_distance), help_distance},
-};
-
-/** \internal Selection method data for the \p within method. */
-gmx_ana_selmethod_t sm_within = {
- "within", GROUP_VALUE, SMETH_DYNAMIC,
- asize(smparams_within), smparams_within,
- &init_data_common,
- NULL,
- &init_common,
- NULL,
- &free_data_common,
- &init_frame_common,
- NULL,
- &evaluate_within,
- {"within REAL of POS_EXPR", asize(help_distance), help_distance},
-};
-
-/*!
- * \param[in] npar Not used (should be 2).
- * \param[in,out] param Method parameters (should point to one of the distance
- * parameter arrays).
- * \returns Pointer to the allocated data (\c t_methoddata_distance).
- *
- * Allocates memory for a \c t_methoddata_distance structure and
- * initializes the parameter as follows:
- * - the first parameter defines the value for
- * \c t_methoddata_distance::cutoff.
- * - the second parameter defines the reference positions and the value is
- * stored in \c t_methoddata_distance::p.
- */
-static void *
-init_data_common(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_distance *data;
-
- snew(data, 1);
- data->cutoff = -1;
- param[0].val.u.r = &data->cutoff;
- param[1].val.u.p = &data->p;
- return data;
-}
-
-/*!
- * \param top Not used.
- * \param npar Not used (should be 2).
- * \param param Method parameters (should point to one of the distance
- * parameter arrays).
- * \param data Pointer to \c t_methoddata_distance to initialize.
- * \returns 0 on success, a non-zero error code on failure.
- *
- * Initializes the neighborhood search data structure
- * (\c t_methoddata_distance::nb).
- * Also checks that the cutoff is valid.
- */
-static int
-init_common(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_distance *d = (t_methoddata_distance *)data;
-
- if ((param[0].flags & SPAR_SET) && d->cutoff <= 0)
- {
- fprintf(stderr, "error: distance cutoff should be > 0");
- return -1;
- }
- return gmx_ana_nbsearch_create(&d->nb, d->cutoff, d->p.nr);
-}
-
-/*!
- * \param data Data to free (should point to a \c t_methoddata_distance).
- *
- * Frees the memory allocated for \c t_methoddata_distance::xref and
- * \c t_methoddata_distance::nb.
- */
-static void
-free_data_common(void *data)
-{
- t_methoddata_distance *d = (t_methoddata_distance *)data;
-
- if (d->nb)
- {
- gmx_ana_nbsearch_free(d->nb);
- }
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Current frame.
- * \param[in] pbc PBC structure.
- * \param data Should point to a \c t_methoddata_distance.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Initializes the neighborhood search for the current frame.
- */
-static int
-init_frame_common(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
-{
- t_methoddata_distance *d = (t_methoddata_distance *)data;
-
- return gmx_ana_nbsearch_pos_init(d->nb, pbc, &d->p);
-}
-
-/*!
- * See sel_updatefunc_pos() for description of the parameters.
- * \p data should point to a \c t_methoddata_distance.
- *
- * Calculates the distance of each position from \c t_methoddata_distance::p
- * and puts them in \p out->u.r.
- */
-static int
-evaluate_distance(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_distance *d = (t_methoddata_distance *)data;
- int b, i;
- real n;
-
- out->nr = pos->g->isize;
- for (b = 0; b < pos->nr; ++b)
- {
- n = gmx_ana_nbsearch_pos_mindist(d->nb, pos, b);
- for (i = pos->m.mapb.index[b]; i < pos->m.mapb.index[b+1]; ++i)
- {
- out->u.r[i] = n;
- }
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_distance.
- *
- * Finds the atoms that are closer than the defined cutoff to
- * \c t_methoddata_distance::xref and puts them in \p out.g.
- */
-static int
-evaluate_within(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_distance *d = (t_methoddata_distance *)data;
- int b;
-
- out->u.g->isize = 0;
- for (b = 0; b < pos->nr; ++b)
- {
- if (gmx_ana_nbsearch_pos_is_within(d->nb, pos, b))
- {
- gmx_ana_pos_append(NULL, out->u.g, pos, b, 0);
- }
- }
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \page sm_insolidangle Selection method: insolidangle
- *
- * This method selects a subset of particles that are located in a solid
- * angle defined by a center and a set of points.
- * The solid angle is constructed as a union of small cones whose axis
- * goes through the center and a point.
- * So there's such a cone for each position, and a
- * point is in the solid angle if it lies within any of these cones.
- * The width of the cones can be adjusted.
- *
- * \internal
- *
- * The method is implemented by partitioning the surface of the unit sphere
- * into bins using the polar coordinates \f$(\theta, \phi)\f$.
- * The partitioning is always uniform in the zenith angle \f$\theta\f$,
- * while the partitioning in the azimuthal angle \f$\phi\f$ varies.
- * For each reference point, the unit vector from the center to the point
- * is constructed, and it is stored in all the bins that overlap with the
- * cone defined by the point.
- * Bins that are completely covered by a single cone are marked as such.
- * Checking whether a point is in the solid angle is then straightforward
- * with this data structure: one finds the bin that corresponds to the point,
- * and checks whether the bin is completely covered. If it is not, one
- * additionally needs to check whether it is within the specified cutoff of
- * any of the stored points.
- *
- * The above construction gives quite a lot of flexibility for constructing
- * the bins without modifying the rest of the code.
- * The current (quite inefficient) implementation is discussed below, but
- * it should be optimized to get the most out of the code.
- *
- * The current way of constructing the bins constructs the boundaries
- * statically: the bin size in the zenith direction is set to approximately
- * half the angle cutoff, and the bins in the azimuthal direction have
- * sizes such that the shortest edge of the bin is approximately equal to
- * half the angle cutoff (for the regions close to the poles, a single bin
- * is used).
- * Each reference point is then added to the bins as follows:
- * -# Find the zenith angle range that is spanned by the cone centered at the
- * point (this is simple addition/subtraction).
- * -# Calculate the maximal span of the cone in the azimuthal direction using
- * the formula
- * \f[\sin \Delta \phi_{max} = \frac{\sin \alpha}{\sin \theta}\f]
- * (a sine formula in spherical coordinates),
- * where \f$\alpha\f$ is the width of the cone and \f$\theta\f$ is the
- * zenith angle of the cone center.
- * Similarly, the zenith angle at which this extent is achieved is
- * calculated using
- * \f[\cos \theta_{max} = \frac{\cos \theta}{\cos \alpha}\f]
- * (Pythagoras's theorem in spherical coordinates).
- * -# For each zenith angle bin that is at least partially covered by the
- * cone, calculate the span of the cone at the edges using
- * \f[\sin^2 \frac{\Delta \phi}{2} = \frac{\sin^2 \frac{\alpha}{2} - \sin^2 \frac{\theta - \theta'}{2}}{\sin \theta \sin \theta'}\f]
- * (distance in spherical geometry),
- * where \f$\theta'\f$ is the zenith angle of the bin edge.
- * -# Using the values calculated above, loop through the azimuthal bins that
- * are partially or completely covered by the cone and update them.
- *
- * The total solid angle (for covered fraction calculations) is estimated by
- * taking the total area of completely covered bins plus
- * half the area of partially covered bins.
- * The second one is an approximation, but should give reasonable estimates
- * for the averages as well as in cases where the bin size is small.
- */
-/*! \internal \file
- * \brief Implementation of the \ref sm_insolidangle "insolidangle"
- * selection method.
- *
- * \todo
- * The implementation could be optimized quite a bit.
- *
- * \todo Move the covered fraction stuff somewhere else and make it more
- * generic (along the lines it is handled in selection.h and trajana.h).
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <math.h>
-
-#include <macros.h>
-#include <maths.h>
-#include <pbc.h>
-#include <physics.h>
-#include <smalloc.h>
-#include <vec.h>
-
-#include <indexutil.h>
-#include <position.h>
-#include <selection.h>
-#include <selmethod.h>
-
-#include "selelem.h"
-
-/*! \internal \brief
- * Internal data structure for the \p insolidangle selection method.
- *
- * \see \c t_partition
- */
-typedef struct
-{
- /** Left edge of the partition. */
- real left;
- /** Bin index corresponding to this partition. */
- int bin;
-} t_partition_item;
-
-/*! \internal \brief
- * Internal data structure for the \p insolidangle selection method.
- *
- * Describes the surface partitioning within one slice along the zenith angle.
- * The slice from azimuthal angle \p p[i].left to \p p[i+1].left belongs to
- * bin \p p[i].bin.
- */
-typedef struct
-{
- /** Number of partition items (\p p contains \p n+1 items). */
- int n;
- /** Array of partition edges and corresponding bins. */
- t_partition_item *p;
-} t_partition;
-
-/*! \internal \brief
- * Internal data structure for the \p insolidangle selection method.
- *
- * Contains the reference points that partially cover a certain region on the
- * surface of the unit sphere.
- * If \p n is -1, the whole region described by the bin is covered.
- */
-typedef struct
-{
- /** Number of points in the array \p x, -1 if whole bin covered. */
- int n;
- /** Number of elements allocated for \p x. */
- int n_alloc;
- /** Array of points that partially cover the bin. */
- rvec *x;
-} t_spheresurfacebin;
-
-/*! \internal \brief
- * Data structure for the \p insolidangle selection method.
- *
- * All angle values are in the units of radians.
- */
-typedef struct
-{
- /** Center of the solid angle. */
- gmx_ana_pos_t center;
- /** Positions that span the solid angle. */
- gmx_ana_pos_t span;
- /** Cutoff angle. */
- real angcut;
- /** Estimate of the covered fraction. */
- real cfrac;
-
- /** Cutoff for the cosine (equals cos(angcut)). */
- real distccut;
- /** Bin size to be used as the target bin size when constructing the bins. */
- real targetbinsize;
-
- /** Number of bins in the \p tbin array. */
- int ntbins;
- /** Size of one bin in the zenith angle direction. */
- real tbinsize;
- /** Array of zenith angle slices. */
- t_partition *tbin;
- /** Number of elements allocated for the \p bin array. */
- int maxbins;
- /** Number of elements used in the \p bin array. */
- int nbins;
- /** Array of individual bins. */
- t_spheresurfacebin *bin;
-} t_methoddata_insolidangle;
-
-/** Allocates data for the \p insolidangle selection method. */
-static void *
-init_data_insolidangle(int npar, gmx_ana_selparam_t *param);
-/** Initializes the \p insolidangle selection method. */
-static int
-init_insolidangle(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Sets the COM/COG data for the \p insolidangle selection method. */
-static void
-set_comg_insolidangle(gmx_ana_pos_t *pos, void *data);
-/** Frees the data allocated for the \p insolidangle selection method. */
-static void
-free_data_insolidangle(void *data);
-/** Initializes the evaluation of the \p insolidangle selection method for a frame. */
-static int
-init_frame_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
-/** Internal helper function for evaluate_insolidangle(). */
-static gmx_bool
-accept_insolidangle(rvec x, t_pbc *pbc, void *data);
-/** Evaluates the \p insolidangle selection method. */
-static int
-evaluate_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
-
-/** Calculates the distance between unit vectors. */
-static real
-sph_distc(rvec x1, rvec x2);
-/** Does a binary search on a \p t_partition to find a bin for a value. */
-static int
-find_partition_bin(t_partition *p, real value);
-/** Finds a bin that corresponds to a location on the unit sphere surface. */
-static int
-find_surface_bin(t_methoddata_insolidangle *surf, rvec x);
-/** Clears/initializes the bins on the unit sphere surface. */
-static void
-clear_surface_points(t_methoddata_insolidangle *surf);
-/** Frees memory allocated for storing the reference points in the surface bins. */
-static void
-free_surface_points(t_methoddata_insolidangle *surf);
-/** Adds a reference point to a given bin. */
-static void
-add_surface_point(t_methoddata_insolidangle *surf, int tbin, int pbin, rvec x);
-/** Marks a bin as completely covered. */
-static void
-mark_surface_covered(t_methoddata_insolidangle *surf, int tbin, int pbin);
-/** Helper function for store_surface_point() to update a single zenith angle bin. */
-static void
-update_surface_bin(t_methoddata_insolidangle *surf, int tbin,
- real phi, real pdelta1, real pdelta2, real pdeltamax,
- rvec x);
-/** Adds a single reference point and updates the surface bins. */
-static void
-store_surface_point(t_methoddata_insolidangle *surf, rvec x);
-/** Optimizes the surface bins for faster searching. */
-static void
-optimize_surface_points(t_methoddata_insolidangle *surf);
-/** Estimates the area covered by the reference cones. */
-static real
-estimate_covered_fraction(t_methoddata_insolidangle *surf);
-/** Checks whether a point lies within a solid angle. */
-static gmx_bool
-is_surface_covered(t_methoddata_insolidangle *surf, rvec x);
-
-/** Parameters for the \p insolidangle selection method. */
-static gmx_ana_selparam_t smparams_insolidangle[] = {
- {"center", {POS_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
- {"span", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
- {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
-};
-
-/** Help text for the \p insolidangle selection method. */
-static const char *help_insolidangle[] = {
- "SELECTING ATOMS IN A SOLID ANGLE[PAR]",
-
- "[TT]insolidangle center POS span POS_EXPR [cutoff REAL][tt][PAR]",
-
- "This keyword selects atoms that are within [TT]REAL[tt] degrees",
- "(default=5) of any position in [TT]POS_EXPR[tt] as seen from [TT]POS[tt]",
- "a position expression that evaluates to a single position), i.e., atoms",
- "in the solid angle spanned by the positions in [TT]POS_EXPR[tt] and",
- "centered at [TT]POS[tt].[PAR]"
-
- "Technically, the solid angle is constructed as a union of small cones",
- "whose tip is at [TT]POS[tt] and the axis goes through a point in",
- "[TT]POS_EXPR[tt]. There is such a cone for each position in",
- "[TT]POS_EXPR[tt], and point is in the solid angle if it lies within any",
- "of these cones. The cutoff determines the width of the cones.",
-};
-
-/** \internal Selection method data for the \p insolidangle method. */
-gmx_ana_selmethod_t sm_insolidangle = {
- "insolidangle", GROUP_VALUE, SMETH_DYNAMIC,
- asize(smparams_insolidangle), smparams_insolidangle,
- &init_data_insolidangle,
- NULL,
- &init_insolidangle,
- NULL,
- &free_data_insolidangle,
- &init_frame_insolidangle,
- NULL,
- &evaluate_insolidangle,
- {"insolidangle center POS span POS_EXPR [cutoff REAL]",
- asize(help_insolidangle), help_insolidangle},
-};
-
-/*!
- * \param[in] npar Not used (should be 3).
- * \param[in,out] param Method parameters (should point to
- * \ref smparams_insolidangle).
- * \returns Pointer to the allocated data (\ref t_methoddata_insolidangle).
- *
- * Allocates memory for a \ref t_methoddata_insolidangle structure and
- * initializes the parameter as follows:
- * - \p center defines the value for t_methoddata_insolidangle::center.
- * - \p span defines the value for t_methoddata_insolidangle::span.
- * - \p cutoff defines the value for t_methoddata_insolidangle::angcut.
- */
-static void *
-init_data_insolidangle(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_insolidangle *data;
-
- snew(data, 1);
- data->angcut = 5.0;
- param[0].val.u.p = &data->center;
- param[1].val.u.p = &data->span;
- param[2].val.u.r = &data->angcut;
- return data;
-}
-
-/*!
- * \param top Not used.
- * \param npar Not used.
- * \param param Not used.
- * \param data Pointer to \ref t_methoddata_insolidangle to initialize.
- * \returns 0 on success, -1 on failure.
- *
- * Converts t_methoddata_insolidangle::angcut to radians and allocates
- * and allocates memory for the bins used during the evaluation.
- */
-static int
-init_insolidangle(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_insolidangle *surf = (t_methoddata_insolidangle *)data;
- int i, c;
-
- if (surf->angcut <= 0)
- {
- fprintf(stderr, "error: angle cutoff should be > 0");
- return -1;
- }
-
- surf->angcut *= DEG2RAD;
-
- surf->distccut = -cos(surf->angcut);
- surf->targetbinsize = surf->angcut / 2;
- surf->ntbins = (int) (M_PI / surf->targetbinsize);
- surf->tbinsize = (180.0 / surf->ntbins)*DEG2RAD;
-
- snew(surf->tbin, (int)(M_PI/surf->tbinsize) + 1);
- surf->maxbins = 0;
- for (i = 0; i < surf->ntbins; ++i)
- {
- c = max(sin(surf->tbinsize*i), sin(surf->tbinsize*(i+1)))
- * M_2PI / surf->targetbinsize + 1;
- snew(surf->tbin[i].p, c+1);
- surf->maxbins += c;
- }
- surf->nbins = 0;
- snew(surf->bin, surf->maxbins);
-
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \ref t_methoddata_insolidangle).
- *
- * Frees the memory allocated for \c t_methoddata_insolidangle::center and
- * \c t_methoddata_insolidangle::span, as well as the memory for the internal
- * bin structure.
- */
-static void
-free_data_insolidangle(void *data)
-{
- t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
- int i;
-
- if (d->tbin)
- {
- for (i = 0; i < d->ntbins; ++i)
- {
- sfree(d->tbin[i].p);
- }
- sfree(d->tbin);
- }
- free_surface_points(d);
- sfree(d->bin);
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Current frame.
- * \param[in] pbc PBC structure.
- * \param data Should point to a \ref t_methoddata_insolidangle.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Creates a lookup structure that enables fast queries of whether a point
- * is within the solid angle or not.
- */
-static int
-init_frame_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
-{
- t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
- rvec dx;
- int i;
-
- free_surface_points(d);
- clear_surface_points(d);
- for (i = 0; i < d->span.nr; ++i)
- {
- if (pbc)
- {
- pbc_dx(pbc, d->span.x[i], d->center.x[0], dx);
- }
- else
- {
- rvec_sub(d->span.x[i], d->center.x[0], dx);
- }
- unitv(dx, dx);
- store_surface_point(d, dx);
- }
- optimize_surface_points(d);
- d->cfrac = -1;
- return 0;
-}
-
-/*!
- * \param[in] x Test point.
- * \param[in] pbc PBC data (if NULL, no PBC are used).
- * \param[in] data Pointer to a \c t_methoddata_insolidangle data structure.
- * \returns TRUE if \p x is within the solid angle, FALSE otherwise.
- */
-static gmx_bool
-accept_insolidangle(rvec x, t_pbc *pbc, void *data)
-{
- t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
- rvec dx;
-
- if (pbc)
- {
- pbc_dx(pbc, x, d->center.x[0], dx);
- }
- else
- {
- rvec_sub(x, d->center.x[0], dx);
- }
- unitv(dx, dx);
- return is_surface_covered(d, dx);
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_insolidangle.
- *
- * Calculates which atoms in \p g are within the solid angle spanned by
- * \c t_methoddata_insolidangle::span and centered at
- * \c t_methoddata_insolidangle::center, and stores the result in \p out->u.g.
- */
-static int
-evaluate_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
- int b;
-
- out->u.g->isize = 0;
- for (b = 0; b < pos->nr; ++b)
- {
- if (accept_insolidangle(pos->x[b], pbc, data))
- {
- gmx_ana_pos_append(NULL, out->u.g, pos, b, 0);
- }
- }
- return 0;
-}
-
-/*!
- * \param[in] sel Selection element to query.
- * \returns TRUE if the covered fraction can be estimated for \p sel with
- * _gmx_selelem_estimate_coverfrac(), FALSE otherwise.
- */
-gmx_bool
-_gmx_selelem_can_estimate_cover(t_selelem *sel)
-{
- t_selelem *child;
- gmx_bool bFound;
- gmx_bool bDynFound;
-
- if (sel->type == SEL_BOOLEAN && sel->u.boolt == BOOL_OR)
- {
- return FALSE;
- }
- bFound = FALSE;
- bDynFound = FALSE;
- child = sel->child;
- while (child)
- {
- if (child->type == SEL_EXPRESSION)
- {
- if (child->u.expr.method->name == sm_insolidangle.name)
- {
- if (bFound || bDynFound)
- {
- return FALSE;
- }
- bFound = TRUE;
- }
- else if (child->u.expr.method
- && (child->u.expr.method->flags & SMETH_DYNAMIC))
- {
- if (bFound)
- {
- return FALSE;
- }
- bDynFound = TRUE;
- }
- }
- else if (!_gmx_selelem_can_estimate_cover(child))
- {
- return FALSE;
- }
- child = child->next;
- }
- return TRUE;
-}
-
-/*!
- * \param[in] sel Selection for which the fraction should be calculated.
- * \returns Fraction of angles covered by the selection (between zero and one).
- *
- * The return value is undefined if _gmx_selelem_can_estimate_cover() returns
- * FALSE.
- * Should be called after gmx_ana_evaluate_selections() has been called for the
- * frame.
- */
-real
-_gmx_selelem_estimate_coverfrac(t_selelem *sel)
-{
- t_selelem *child;
- real cfrac;
-
- if (sel->type == SEL_EXPRESSION && sel->u.expr.method->name == sm_insolidangle.name)
- {
- t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)sel->u.expr.mdata;
- if (d->cfrac < 0)
- {
- d->cfrac = estimate_covered_fraction(d);
- }
- return d->cfrac;
- }
- if (sel->type == SEL_BOOLEAN && sel->u.boolt == BOOL_NOT)
- {
- cfrac = _gmx_selelem_estimate_coverfrac(sel->child);
- if (cfrac < 1.0)
- {
- return 1 - cfrac;
- }
- return 1;
- }
-
- /* Here, we assume that the selection is simple enough */
- child = sel->child;
- while (child)
- {
- cfrac = _gmx_selelem_estimate_coverfrac(child);
- if (cfrac < 1.0)
- {
- return cfrac;
- }
- child = child->next;
- }
- return 1.0;
-}
-
-/*!
- * \param[in] x1 Unit vector 1.
- * \param[in] x2 Unit vector 2.
- * \returns Minus the dot product of \p x1 and \p x2.
- *
- * This function is used internally to calculate the distance between the
- * unit vectors \p x1 and \p x2 to find out whether \p x2 is within the
- * cone centered at \p x1. Currently, the cosine of the angle is used
- * for efficiency, and the minus is there to make it behave like a normal
- * distance (larger values mean longer distances).
- */
-static real
-sph_distc(rvec x1, rvec x2)
-{
- return -iprod(x1, x2);
-}
-
-/*!
- * \param[in] p Partition to search.
- * \param[in] value Value to search for.
- * \returns The partition index in \p p that contains \p value.
- *
- * If \p value is outside the range of \p p, the first/last index is returned.
- * Otherwise, the return value \c i satisfies \c p->p[i].left<=value and
- * \c p->p[i+1].left>value
- */
-static int
-find_partition_bin(t_partition *p, real value)
-{
- int pmin, pmax, pbin;
-
- /* Binary search the partition */
- pmin = 0; pmax = p->n;
- while (pmax > pmin + 1)
- {
- pbin = pmin + (pmax - pmin) / 2;
- if (p->p[pbin].left <= value)
- {
- pmin = pbin;
- }
- else
- {
- pmax = pbin;
- }
- }
- pbin = pmin;
- return pbin;
-}
-
-/*!
- * \param[in] surf Surface data structure to search.
- * \param[in] x Unit vector to find.
- * \returns The bin index that contains \p x.
- *
- * The return value is an index to the \p surf->bin array.
- */
-static int
-find_surface_bin(t_methoddata_insolidangle *surf, rvec x)
-{
- real theta, phi;
- int tbin, pbin;
-
- theta = acos(x[ZZ]);
- phi = atan2(x[YY], x[XX]);
- tbin = floor(theta / surf->tbinsize);
- if (tbin >= surf->ntbins)
- {
- tbin = surf->ntbins - 1;
- }
- pbin = find_partition_bin(&surf->tbin[tbin], phi);
- return surf->tbin[tbin].p[pbin].bin;
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- *
- * Clears the reference points from the bins and (re)initializes the edges
- * of the azimuthal bins.
- */
-static void
-clear_surface_points(t_methoddata_insolidangle *surf)
-{
- int i, j, c;
-
- surf->nbins = 0;
- for (i = 0; i < surf->ntbins; ++i)
- {
- c = min(sin(surf->tbinsize*i), sin(surf->tbinsize*(i+1)))
- * M_2PI / surf->targetbinsize + 1;
- if (c <= 0)
- {
- c = 1;
- }
- surf->tbin[i].n = c;
- for (j = 0; j < c; ++j)
- {
- surf->tbin[i].p[j].left = -M_PI + j*M_2PI/c - 0.0001;
- surf->tbin[i].p[j].bin = surf->nbins;
- surf->bin[surf->nbins].n = 0;
- surf->nbins++;
- }
- surf->tbin[i].p[c].left = M_PI + 0.0001;
- surf->tbin[i].p[c].bin = -1;
- }
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- */
-static void
-free_surface_points(t_methoddata_insolidangle *surf)
-{
- int i;
-
- for (i = 0; i < surf->nbins; ++i)
- {
- if (surf->bin[i].x)
- {
- sfree(surf->bin[i].x);
- }
- surf->bin[i].n_alloc = 0;
- surf->bin[i].x = NULL;
- }
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- * \param[in] tbin Bin number in the zenith angle direction.
- * \param[in] pbin Bin number in the azimuthal angle direction.
- * \param[in] x Point to store.
- */
-static void
-add_surface_point(t_methoddata_insolidangle *surf, int tbin, int pbin, rvec x)
-{
- int bin;
-
- bin = surf->tbin[tbin].p[pbin].bin;
- /* Return if bin is already completely covered */
- if (surf->bin[bin].n == -1)
- return;
- /* Allocate more space if necessary */
- if (surf->bin[bin].n == surf->bin[bin].n_alloc) {
- surf->bin[bin].n_alloc += 10;
- srenew(surf->bin[bin].x, surf->bin[bin].n_alloc);
- }
- /* Add the point to the bin */
- copy_rvec(x, surf->bin[bin].x[surf->bin[bin].n]);
- ++surf->bin[bin].n;
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- * \param[in] tbin Bin number in the zenith angle direction.
- * \param[in] pbin Bin number in the azimuthal angle direction.
- */
-static void
-mark_surface_covered(t_methoddata_insolidangle *surf, int tbin, int pbin)
-{
- int bin;
-
- bin = surf->tbin[tbin].p[pbin].bin;
- surf->bin[bin].n = -1;
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- * \param[in] tbin Bin number in the zenith angle direction.
- * \param[in] phi Azimuthal angle of \p x.
- * \param[in] pdelta1 Width of the cone at the lower edge of \p tbin.
- * \param[in] pdelta2 Width of the cone at the uppper edge of \p tbin.
- * \param[in] pdeltamax Max. width of the cone inside \p tbin.
- * \param[in] x Point to store (should have unit length).
- */
-static void
-update_surface_bin(t_methoddata_insolidangle *surf, int tbin,
- real phi, real pdelta1, real pdelta2, real pdeltamax,
- rvec x)
-{
- real pdelta, phi1, phi2;
- int pbin1, pbin2, pbin;
-
- /* Find the edges of the bins affected */
- pdelta = max(max(pdelta1, pdelta2), pdeltamax);
- phi1 = phi - pdelta;
- if (phi1 < -M_PI)
- {
- phi1 += M_2PI;
- }
- phi2 = phi + pdelta;
- if (phi2 > M_PI)
- {
- phi2 -= M_2PI;
- }
- pbin1 = find_partition_bin(&surf->tbin[tbin], phi1);
- pbin2 = find_partition_bin(&surf->tbin[tbin], phi2);
- /* Find the edges of completely covered region */
- pdelta = min(pdelta1, pdelta2);
- phi1 = phi - pdelta;
- if (phi1 < -M_PI)
- {
- phi1 += M_2PI;
- }
- phi2 = phi + pdelta;
- /* Loop over all affected bins */
- pbin = pbin1;
- do
- {
- /* Wrap bin around if end reached */
- if (pbin == surf->tbin[tbin].n)
- {
- pbin = 0;
- phi1 -= M_2PI;
- phi2 -= M_2PI;
- }
- /* Check if bin is completely covered and update */
- if (surf->tbin[tbin].p[pbin].left >= phi1
- && surf->tbin[tbin].p[pbin+1].left <= phi2)
- {
- mark_surface_covered(surf, tbin, pbin);
- }
- else
- {
- add_surface_point(surf, tbin, pbin, x);
- }
- }
- while (pbin++ != pbin2); /* Loop including pbin2 */
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- * \param[in] x Point to store (should have unit length).
- *
- * Finds all the bins covered by the cone centered at \p x and calls
- * update_surface_bin() to update them.
- */
-static void
-store_surface_point(t_methoddata_insolidangle *surf, rvec x)
-{
- real theta, phi;
- real pdeltamax, tmax;
- real theta1, theta2, pdelta1, pdelta2;
- int tbin, pbin, bin;
-
- theta = acos(x[ZZ]);
- phi = atan2(x[YY], x[XX]);
- /* Find the maximum extent in the phi direction */
- if (theta <= surf->angcut)
- {
- pdeltamax = M_PI;
- tmax = 0;
- }
- else if (theta >= M_PI - surf->angcut)
- {
- pdeltamax = M_PI;
- tmax = M_PI;
- }
- else
- {
- pdeltamax = asin(sin(surf->angcut) / sin(theta));
- tmax = acos(cos(theta) / cos(surf->angcut));
- }
- /* Find the first affected bin */
- tbin = max(floor((theta - surf->angcut) / surf->tbinsize), 0);
- theta1 = tbin * surf->tbinsize;
- if (theta1 < theta - surf->angcut)
- {
- pdelta1 = 0;
- }
- else
- {
- pdelta1 = M_PI;
- }
- /* Loop through all affected bins */
- while (tbin < ceil((theta + surf->angcut) / surf->tbinsize)
- && tbin < surf->ntbins)
- {
- /* Calculate the next boundaries */
- theta2 = (tbin+1) * surf->tbinsize;
- if (theta2 > theta + surf->angcut)
- {
- pdelta2 = 0;
- }
- else if (tbin == surf->ntbins - 1)
- {
- pdelta2 = M_PI;
- }
- else
- {
- pdelta2 = 2*asin(sqrt(
- (sqr(sin(surf->angcut/2)) - sqr(sin((theta2-theta)/2))) /
- (sin(theta) * sin(theta2))));
- }
- /* Update the bin */
- if (tmax >= theta1 && tmax <= theta2)
- {
- update_surface_bin(surf, tbin, phi, pdelta1, pdelta2, pdeltamax, x);
- }
- else
- {
- update_surface_bin(surf, tbin, phi, pdelta1, pdelta2, 0, x);
- }
- /* Next bin */
- theta1 = theta2;
- pdelta1 = pdelta2;
- ++tbin;
- }
-}
-
-/*!
- * \param[in,out] surf Surface data structure.
- *
- * Currently, this function does nothing.
- */
-static void
-optimize_surface_points(t_methoddata_insolidangle *surf)
-{
- /* TODO: Implement */
-}
-
-/*!
- * \param[in] surf Surface data structure.
- * \returns An estimate for the area covered by the reference points.
- */
-static real
-estimate_covered_fraction(t_methoddata_insolidangle *surf)
-{
- int t, p, n;
- real cfrac, tfrac, pfrac;
-
- cfrac = 0.0;
- for (t = 0; t < surf->ntbins; ++t)
- {
- tfrac = cos(t * surf->tbinsize) - cos((t+1) * surf->tbinsize);
- for (p = 0; p < surf->tbin[t].n; ++p)
- {
- pfrac = surf->tbin[t].p[p+1].left - surf->tbin[t].p[p].left;
- n = surf->bin[surf->tbin[t].p[p].bin].n;
- if (n == -1) /* Bin completely covered */
- {
- cfrac += tfrac * pfrac;
- }
- else if (n > 0) /* Bin partially covered */
- {
- cfrac += tfrac * pfrac / 2; /* A rough estimate */
- }
- }
- }
- return cfrac / (4*M_PI);
-}
-
-/*!
- * \param[in] surf Surface data structure to search.
- * \param[in] x Unit vector to check.
- * \returns TRUE if \p x is within the solid angle, FALSE otherwise.
- */
-static gmx_bool
-is_surface_covered(t_methoddata_insolidangle *surf, rvec x)
-{
- int bin, i;
-
- bin = find_surface_bin(surf, x);
- /* Check for completely covered bin */
- if (surf->bin[bin].n == -1)
- {
- return TRUE;
- }
- /* Check each point that partially covers the bin */
- for (i = 0; i < surf->bin[bin].n; ++i)
- {
- if (sph_distc(x, surf->bin[bin].x[i]) < surf->distccut)
- {
- return TRUE;
- }
- }
- return FALSE;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementations of internal selection methods for numeric and
- * string keyword evaluation.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h> /*old Mac needs types before regex.h*/
-#endif
-#ifdef HAVE_REGEX_H
-#include <regex.h>
-#define USE_REGEX
-#endif
-
-#include <gmx_fatal.h>
-#include <macros.h>
-#include <smalloc.h>
-#include <string2.h>
-
-#include <selmethod.h>
-
-#include "keywords.h"
-#include "parsetree.h"
-#include "selelem.h"
-
-/** Allocates data for integer keyword evaluation. */
-static void *
-init_data_kwint(int npar, gmx_ana_selparam_t *param);
-/** Allocates data for real keyword evaluation. */
-static void *
-init_data_kwreal(int npar, gmx_ana_selparam_t *param);
-/** Allocates data for string keyword evaluation. */
-static void *
-init_data_kwstr(int npar, gmx_ana_selparam_t *param);
-/** Initializes data for integer keyword evaluation. */
-static int
-init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes data for real keyword evaluation. */
-static int
-init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes data for string keyword evaluation. */
-static int
-init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Frees the memory allocated for string keyword evaluation. */
-static void
-free_data_kwstr(void *data);
-/** Evaluates integer selection keywords. */
-static int
-evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates real selection keywords. */
-static int
-evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates string selection keywords. */
-static int
-evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-
-/*! \internal \brief
- * Data structure for integer keyword expression evaluation.
- */
-typedef struct t_methoddata_kwint
-{
- /** Array of values for the keyword. */
- int *v;
- /** Number of ranges in the \p r array. */
- int n;
- /*! \brief
- * Array of sorted integer ranges to match against.
- *
- * Each range is made of two integers, giving the endpoints (inclusive).
- * This field stores the pointer to the ranges allocated by the
- * parameter parser; see \ref SPAR_RANGES for more information.
- */
- int *r;
-} t_methoddata_kwint;
-
-/*! \internal \brief
- * Data structure for real keyword expression evaluation.
- */
-typedef struct t_methoddata_kwreal
-{
- /** Array of values for the keyword. */
- real *v;
- /** Number of ranges in the \p r array. */
- int n;
- /*! \brief
- * Array of sorted ranges to match against.
- *
- * Each range is made of two values, giving the endpoints (inclusive).
- * This field stores the pointer to the ranges allocated by the
- * parameter parser; see \ref SPAR_RANGES for more information.
- */
- real *r;
-} t_methoddata_kwreal;
-
-/*! \internal \brief
- * Data structure for string keyword expression evaluation.
- */
-typedef struct t_methoddata_kwstr
-{
- /** Array of values for the keyword. */
- char **v;
- /** Number of elements in the \p val array. */
- int n;
- /*! \internal \brief
- * Array of strings/regular expressions to match against.
- */
- struct t_methoddata_kwstr_match {
- /** TRUE if the expression is a regular expression, FALSE otherwise. */
- gmx_bool bRegExp;
- /** The value to match against. */
- union {
-#ifdef USE_REGEX
- /** Compiled regular expression if \p bRegExp is TRUE. */
- regex_t r;
-#endif
- /** The string if \p bRegExp is FALSE; */
- char *s;
- } u;
- } *m;
-} t_methoddata_kwstr;
-
-/** Parameters for integer keyword evaluation. */
-static gmx_ana_selparam_t smparams_keyword_int[] = {
- {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
- {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
-};
-
-/** Parameters for real keyword evaluation. */
-static gmx_ana_selparam_t smparams_keyword_real[] = {
- {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL | SPAR_DYNAMIC},
- {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
-};
-
-/** Parameters for string keyword evaluation. */
-static gmx_ana_selparam_t smparams_keyword_str[] = {
- {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
- {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
-};
-
-/** \internal Selection method data for integer keyword evaluation. */
-gmx_ana_selmethod_t sm_keyword_int = {
- "kw_int", GROUP_VALUE, SMETH_SINGLEVAL,
- asize(smparams_keyword_int), smparams_keyword_int,
- &init_data_kwint,
- NULL,
- &init_kwint,
- NULL,
- NULL,
- NULL,
- &evaluate_keyword_int,
- NULL,
- {NULL, 0, NULL},
-};
-
-/** \internal Selection method data for real keyword evaluation. */
-gmx_ana_selmethod_t sm_keyword_real = {
- "kw_real", GROUP_VALUE, SMETH_SINGLEVAL,
- asize(smparams_keyword_real), smparams_keyword_real,
- &init_data_kwreal,
- NULL,
- &init_kwreal,
- NULL,
- NULL,
- NULL,
- &evaluate_keyword_real,
- NULL,
- {NULL, 0, NULL},
-};
-
-/** \internal Selection method data for string keyword evaluation. */
-gmx_ana_selmethod_t sm_keyword_str = {
- "kw_str", GROUP_VALUE, SMETH_SINGLEVAL,
- asize(smparams_keyword_str), smparams_keyword_str,
- &init_data_kwstr,
- NULL,
- &init_kwstr,
- NULL,
- &free_data_kwstr,
- NULL,
- &evaluate_keyword_str,
- NULL,
- {NULL, 0, NULL},
-};
-
-/** Initializes keyword evaluation for an arbitrary group. */
-static int
-init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes output for keyword evaluation in an arbitrary group. */
-static int
-init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data);
-/** Frees the data allocated for keyword evaluation in an arbitrary group. */
-static void
-free_data_kweval(void *data);
-/** Initializes frame evaluation for keyword evaluation in an arbitrary group. */
-static int
-init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
-/** Evaluates keywords in an arbitrary group. */
-static int
-evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-
-/*! \internal \brief
- * Data structure for keyword evaluation in arbitrary groups.
- */
-typedef struct
-{
- /** Wrapped keyword method for evaluating the values. */
- gmx_ana_selmethod_t *kwmethod;
- /** Method data for \p kwmethod. */
- void *kwmdata;
- /** Group in which \p kwmethod should be evaluated. */
- gmx_ana_index_t g;
-} t_methoddata_kweval;
-
-/** Parameters for keyword evaluation in an arbitrary group. */
-static gmx_ana_selparam_t smparams_kweval[] = {
- {NULL, {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
-};
-
-
-/********************************************************************
- * INTEGER KEYWORD EVALUATION
- ********************************************************************/
-
-/*!
- * \param[in] npar Not used.
- * \param param Not used.
- * \returns Pointer to the allocated data (\ref t_methoddata_kwint).
- *
- * Allocates memory for a \ref t_methoddata_kwint structure.
- */
-static void *
-init_data_kwint(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_kwint *data;
-
- snew(data, 1);
- return data;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used (should be 2).
- * \param[in] param Method parameters (should point to \ref smparams_keyword_int).
- * \param[in] data Should point to \ref t_methoddata_kwint.
- * \returns 0 (the initialization always succeeds).
- */
-static int
-init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_kwint *d = (t_methoddata_kwint *)data;
-
- d->v = param[0].val.u.i;
- d->n = param[1].val.nr;
- d->r = param[1].val.u.i;
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_kwint.
- *
- * Does a binary search to find which atoms match the ranges in the
- * \c t_methoddata_kwint structure for this selection.
- * Matching atoms are stored in \p out->u.g.
- */
-static int
-evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_kwint *d = (t_methoddata_kwint *)data;
- int n, i, j, jmin, jmax;
- int val;
-
- out->u.g->isize = 0;
- n = d->n;
- for (i = 0; i < g->isize; ++i)
- {
- val = d->v[i];
- if (d->r[0] > val || d->r[2*n-1] < val)
- {
- continue;
- }
- jmin = 0;
- jmax = n;
- while (jmax - jmin > 1)
- {
- j = jmin + (jmax - jmin) / 2;
- if (val < d->r[2*j])
- {
- jmax = j;
- }
- else
- {
- jmin = j;
- if (val <= d->r[2*j+1])
- {
- break;
- }
- /* ++jmin;*/
- }
- }
- if (val <= d->r[2*jmin+1])
- {
- out->u.g->index[out->u.g->isize++] = g->index[i];
- }
- }
- return 0;
-}
-
-
-/********************************************************************
- * REAL KEYWORD EVALUATION
- ********************************************************************/
-
-/*!
- * \param[in] npar Not used.
- * \param param Not used.
- * \returns Pointer to the allocated data (\ref t_methoddata_kwreal).
- *
- * Allocates memory for a \ref t_methoddata_kwreal structure.
- */
-static void *
-init_data_kwreal(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_kwreal *data;
-
- snew(data, 1);
- return data;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used (should be 2).
- * \param[in] param Method parameters (should point to \ref smparams_keyword_real).
- * \param[in] data Should point to \ref t_methoddata_kwreal.
- * \returns 0 (the initialization always succeeds).
- */
-static int
-init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
-
- d->v = param[0].val.u.r;
- d->n = param[1].val.nr;
- d->r = param[1].val.u.r;
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_kwreal.
- *
- * Does a binary search to find which atoms match the ranges in the
- * \c t_methoddata_kwreal structure for this selection.
- * Matching atoms are stored in \p out->u.g.
- */
-static int
-evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
- int n, i, j, jmin, jmax;
- real val;
-
- out->u.g->isize = 0;
- n = d->n;
- for (i = 0; i < g->isize; ++i)
- {
- val = d->v[i];
- if (d->r[0] > val || d->r[2*n-1] < val)
- {
- continue;
- }
- jmin = 0;
- jmax = n;
- while (jmax - jmin > 1)
- {
- j = jmin + (jmax - jmin) / 2;
- if (val < d->r[2*j])
- {
- jmax = j;
- }
- else
- {
- jmin = j;
- if (val <= d->r[2*j+1])
- {
- break;
- }
- /* ++jmin;*/
- }
- }
- if (val <= d->r[2*jmin+1])
- {
- out->u.g->index[out->u.g->isize++] = g->index[i];
- }
- }
- return 0;
-}
-
-
-/********************************************************************
- * STRING KEYWORD EVALUATION
- ********************************************************************/
-
-/*!
- * \param[in] npar Not used.
- * \param param Not used.
- * \returns Pointer to the allocated data (\ref t_methoddata_kwstr).
- *
- * Allocates memory for a \ref t_methoddata_kwstr structure.
- */
-static void *
-init_data_kwstr(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_kwstr *data;
-
- snew(data, 1);
- return data;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used (should be 2).
- * \param[in] param Method parameters (should point to \ref smparams_keyword_str).
- * \param[in] data Should point to \ref t_methoddata_kwstr.
- * \returns 0 (the initialization always succeeds).
- */
-static int
-init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
- char *buf;
- char *s;
- int i;
- size_t j;
- gmx_bool bRegExp;
-
- d->v = param[0].val.u.s;
- d->n = param[1].val.nr;
- /* Return if this is not the first time */
- if (d->m)
- {
- return 0;
- }
- snew(d->m, d->n);
- for (i = 0; i < d->n; ++i)
- {
- s = param[1].val.u.s[i];
- bRegExp = FALSE;
- for (j = 0; j < strlen(s); ++j)
- {
- if (ispunct(s[j]) && s[j] != '?' && s[j] != '*')
- {
- bRegExp = TRUE;
- break;
- }
- }
- if (bRegExp)
- {
-#ifdef USE_REGEX
- snew(buf, strlen(s) + 3);
- sprintf(buf, "^%s$", s);
- if (regcomp(&d->m[i].u.r, buf, REG_EXTENDED | REG_NOSUB))
- {
- bRegExp = FALSE;
- fprintf(stderr, "WARNING: error in regular expression,\n"
- " will match '%s' as a simple string\n", s);
- }
- sfree(buf);
-#else
- bRegExp = FALSE;
- fprintf(stderr, "WARNING: no regular expressions support,\n"
- " will match '%s' as a simple string\n", s);
-#endif
- }
- if (!bRegExp)
- {
- d->m[i].u.s = s;
- }
- d->m[i].bRegExp = bRegExp;
- }
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \ref t_methoddata_kwstr).
- *
- * Frees the memory allocated for t_methoddata_kwstr::val.
- */
-static void
-free_data_kwstr(void *data)
-{
- t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
- int i;
-
- for (i = 0; i < d->n; ++i)
- {
- if (d->m[i].bRegExp)
- {
-#ifdef USE_REGEX
- /* This branch should only be taken if regular expressions
- * are available, but the ifdef is still needed. */
- regfree(&d->m[i].u.r);
-#endif
- }
- }
- sfree(d->m);
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_kwstr.
- *
- * Does a linear search to find which atoms match the strings in the
- * \c t_methoddata_kwstr structure for this selection.
- * Wildcards are allowed in the strings.
- * Matching atoms are stored in \p out->u.g.
- */
-static int
-evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
- int i, j;
- gmx_bool bFound;
-
- out->u.g->isize = 0;
- for (i = 0; i < g->isize; ++i)
- {
- bFound = FALSE;
- for (j = 0; j < d->n && !bFound; ++j)
- {
- if (d->m[j].bRegExp)
- {
-#ifdef USE_REGEX
- /* This branch should only be taken if regular expressions
- * are available, but the ifdef is still needed. */
- if (!regexec(&d->m[j].u.r, d->v[i], 0, NULL, 0))
- {
- bFound = TRUE;
- }
-#endif
- }
- else
- {
- if (gmx_wcmatch(d->m[j].u.s, d->v[i]) == 0)
- {
- bFound = TRUE;
- }
- }
- }
- if (bFound)
- {
- out->u.g->index[out->u.g->isize++] = g->index[i];
- }
- }
- return 0;
-}
-
-
-/********************************************************************
- * KEYWORD EVALUATION FOR ARBITRARY GROUPS
- ********************************************************************/
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used.
- * \param[in] param Not used.
- * \param[in] data Should point to \ref t_methoddata_kweval.
- * \returns 0 on success, a non-zero error code on return.
- *
- * Calls the initialization method of the wrapped keyword.
- */
-static int
-init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_kweval *d = (t_methoddata_kweval *)data;
-
- return d->kwmethod->init(top, 0, NULL, d->kwmdata);
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in,out] out Pointer to output data structure.
- * \param[in,out] data Should point to \c t_methoddata_kweval.
- * \returns 0 for success.
- */
-static int
-init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_kweval *d = (t_methoddata_kweval *)data;
-
- out->nr = d->g.isize;
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \c t_methoddata_kweval).
- *
- * Frees the memory allocated for all the members of \c t_methoddata_kweval.
- */
-static void
-free_data_kweval(void *data)
-{
- t_methoddata_kweval *d = (t_methoddata_kweval *)data;
-
- _gmx_selelem_free_method(d->kwmethod, d->kwmdata);
-}
-
-/*!
- * \param[in] top Topology.
- * \param[in] fr Current frame.
- * \param[in] pbc PBC structure.
- * \param data Should point to a \ref t_methoddata_kweval.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Creates a lookup structure that enables fast queries of whether a point
- * is within the solid angle or not.
- */
-static int
-init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
-{
- t_methoddata_kweval *d = (t_methoddata_kweval *)data;
-
- return d->kwmethod->init_frame(top, fr, pbc, d->kwmdata);
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_kweval.
- *
- * Calls the evaluation function of the wrapped keyword with the given
- * parameters, with the exception of using \c t_methoddata_kweval::g for the
- * evaluation group.
- */
-static int
-evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_kweval *d = (t_methoddata_kweval *)data;
-
- return d->kwmethod->update(top, fr, pbc, &d->g, out, d->kwmdata);
-}
-
-/*!
- * \param[out] selp Pointer to receive a pointer to the created selection
- * element (set to NULL on error).
- * \param[in] method Keyword selection method to evaluate.
- * \param[in] param Parameter that gives the group to evaluate \p method in.
- * \param[in] scanner Scanner data structure.
- * \returns 0 on success, non-zero error code on error.
- *
- * Creates a \ref SEL_EXPRESSION selection element (pointer put in \c *selp)
- * that evaluates the keyword method given by \p method in the group given by
- * \p param.
- */
-int
-_gmx_sel_init_keyword_evaluator(t_selelem **selp, gmx_ana_selmethod_t *method,
- t_selexpr_param *param, void *scanner)
-{
- t_selelem *sel;
- t_methoddata_kweval *data;
-
- if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
- || method->outinit || method->pupdate)
- {
- _gmx_selexpr_free_params(param);
- gmx_incons("unsupported keyword method for arbitrary group evaluation");
- return -1;
- }
-
- *selp = NULL;
- sel = _gmx_selelem_create(SEL_EXPRESSION);
- _gmx_selelem_set_method(sel, method, scanner);
-
- snew(data, 1);
- data->kwmethod = sel->u.expr.method;
- data->kwmdata = sel->u.expr.mdata;
- gmx_ana_index_clear(&data->g);
-
- snew(sel->u.expr.method, 1);
- memcpy(sel->u.expr.method, data->kwmethod, sizeof(gmx_ana_selmethod_t));
- sel->u.expr.method->flags |= SMETH_VARNUMVAL;
- sel->u.expr.method->init_data = NULL;
- sel->u.expr.method->set_poscoll = NULL;
- sel->u.expr.method->init = method->init ? &init_kweval : NULL;
- sel->u.expr.method->outinit = &init_output_kweval;
- sel->u.expr.method->free = &free_data_kweval;
- sel->u.expr.method->init_frame = method->init_frame ? &init_frame_kweval : NULL;
- sel->u.expr.method->update = &evaluate_kweval;
- sel->u.expr.method->pupdate = NULL;
- sel->u.expr.method->nparams = asize(smparams_kweval);
- sel->u.expr.method->param = smparams_kweval;
- _gmx_selelem_init_method_params(sel, scanner);
- sel->u.expr.mdata = data;
-
- sel->u.expr.method->param[0].val.u.g = &data->g;
-
- sfree(param->name);
- param->name = NULL;
- if (!_gmx_sel_parse_params(param, sel->u.expr.method->nparams,
- sel->u.expr.method->param, sel, scanner))
- {
- _gmx_selelem_free(sel);
- return -1;
- }
- *selp = sel;
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of the merging selection modifier.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <macros.h>
-#include <smalloc.h>
-#include <vec.h>
-
-#include <position.h>
-#include <selmethod.h>
-
-/*! \internal \brief
- * Data structure for the merging selection modifiers.
- */
-typedef struct
-{
- /** Input positions. */
- gmx_ana_pos_t p1;
- /** Other input positions. */
- gmx_ana_pos_t p2;
- /** Group to store the output atom indices. */
- gmx_ana_index_t g;
- /** Stride for merging (\c stride values from \c p1 for each in \c p2). */
- int stride;
-} t_methoddata_merge;
-
-/** Allocates data for the merging selection modifiers. */
-static void *
-init_data_merge(int npar, gmx_ana_selparam_t *param);
-/** Initializes data for the merging selection modifiers. */
-static int
-init_merge(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes output for the \p merge selection modifier. */
-static int
-init_output_merge(t_topology *top, gmx_ana_selvalue_t *out, void *data);
-/** Initializes output for the \p plus selection modifier. */
-static int
-init_output_plus(t_topology *top, gmx_ana_selvalue_t *out, void *data);
-/** Frees the memory allocated for the merging selection modifiers. */
-static void
-free_data_merge(void *data);
-/** Evaluates the \p merge selection modifier. */
-static int
-evaluate_merge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p plus selection modifier. */
-static int
-evaluate_plus(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data);
-
-/** Parameters for the merging selection modifiers. */
-static gmx_ana_selparam_t smparams_merge[] = {
- {NULL, {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
- {NULL, {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
- {"stride", {INT_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
-};
-
-/** Help text for the merging selection modifiers. */
-static const char *help_merge[] = {
- "MERGING SELECTIONS[PAR]",
-
- "[TT]POSEXPR merge POSEXPR [stride INT][tt][BR]",
- "[TT]POSEXPR merge POSEXPR [merge POSEXPR ...][tt][BR]",
- "[TT]POSEXPR plus POSEXPR [plus POSEXPR ...][tt][PAR]",
-
- "Basic selection keywords can only create selections where each atom",
- "occurs at most once. The [TT]merge[tt] and [TT]plus[tt] selection",
- "keywords can be used to work around this limitation. Both create",
- "a selection that contains the positions from all the given position",
- "expressions, even if they contain duplicates.",
- "The difference between the two is that [TT]merge[tt] expects two or more",
- "selections with the same number of positions, and the output contains",
- "the input positions selected from each expression in turn, i.e.,",
- "the output is like A1 B1 A2 B2 and so on. It is also possible to merge",
- "selections of unequal size as long as the size of the first is a",
- "multiple of the second one. The [TT]stride[tt] parameter can be used",
- "to explicitly provide this multiplicity.",
- "[TT]plus[tt] simply concatenates the positions after each other, and",
- "can work also with selections of different sizes.",
- "These keywords are valid only at the selection level, not in any",
- "subexpressions.[PAR]",
-};
-
-/** \internal Selection method data for the \p plus modifier. */
-gmx_ana_selmethod_t sm_merge = {
- "merge", POS_VALUE, SMETH_MODIFIER,
- asize(smparams_merge), smparams_merge,
- &init_data_merge,
- NULL,
- &init_merge,
- &init_output_merge,
- &free_data_merge,
- NULL,
- NULL,
- &evaluate_merge,
- {"merge POSEXPR", asize(help_merge), help_merge},
-};
-
-/** \internal Selection method data for the \p plus modifier. */
-gmx_ana_selmethod_t sm_plus = {
- "plus", POS_VALUE, SMETH_MODIFIER,
- asize(smparams_merge)-1, smparams_merge,
- &init_data_merge,
- NULL,
- &init_merge,
- &init_output_plus,
- &free_data_merge,
- NULL,
- NULL,
- &evaluate_plus,
- {"plus POSEXPR", asize(help_merge), help_merge},
-};
-
-/*!
- * \param[in] npar Should be 2 for \c plus and 3 for \c merge.
- * \param[in,out] param Method parameters (should point to a copy of
- * \ref smparams_merge).
- * \returns Pointer to the allocated data (\p t_methoddata_merge).
- *
- * Allocates memory for a \p t_methoddata_merge structure.
- */
-static void *
-init_data_merge(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_merge *data;
-
- snew(data, 1);
- data->stride = 0;
- param[0].val.u.p = &data->p1;
- param[1].val.u.p = &data->p2;
- if (npar > 2)
- {
- param[2].val.u.i = &data->stride;
- }
- return data;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used (should be 2 or 3).
- * \param[in] param Method parameters (should point to \ref smparams_merge).
- * \param[in] data Should point to a \p t_methoddata_merge.
- * \returns 0 if everything is successful, -1 on error.
- */
-static int
-init_merge(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
- int i;
-
- if (d->stride < 0)
- {
- fprintf(stderr, "error: stride for merging should be positive\n");
- return -1;
- }
- /* If no stride given, deduce it from the input sizes */
- if (d->stride == 0)
- {
- d->stride = d->p1.nr / d->p2.nr;
- }
- if (d->p1.nr != d->stride*d->p2.nr)
- {
- fprintf(stderr, "error: the number of positions to be merged are not compatible\n");
- return -1;
- }
- /* We access the m.b.nra field instead of g->isize in the position
- * data structures to handle cases where g is NULL
- * (this occurs with constant positions. */
- gmx_ana_index_reserve(&d->g, d->p1.m.b.nra + d->p2.m.b.nra);
- d->g.isize = d->p1.m.b.nra + d->p2.m.b.nra;
- return 0;
-}
-
-/*! \brief
- * Does common initialization to all merging modifiers.
- *
- * \param[in] top Topology data structure.
- * \param[in,out] out Pointer to output data structure.
- * \param[in,out] data Should point to \c t_methoddata_merge.
- * \returns 0 for success.
- */
-static int
-init_output_common(t_topology *top, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
-
- if (d->p1.m.type != d->p2.m.type)
- {
- /* TODO: Maybe we could pick something else here? */
- out->u.p->m.type = INDEX_UNKNOWN;
- }
- else
- {
- out->u.p->m.type = d->p1.m.type;
- }
- gmx_ana_pos_reserve(out->u.p, d->p1.nr + d->p2.nr, d->g.isize);
- if (d->p1.v)
- {
- gmx_ana_pos_reserve_velocities(out->u.p);
- }
- if (d->p1.f)
- {
- gmx_ana_pos_reserve_forces(out->u.p);
- }
- gmx_ana_pos_set_evalgrp(out->u.p, &d->g);
- gmx_ana_pos_empty_init(out->u.p);
- d->g.isize = 0;
- return 0;
-}
-
-/*!
- * \param[in] top Topology data structure.
- * \param[in,out] out Pointer to output data structure.
- * \param[in,out] data Should point to \c t_methoddata_merge.
- * \returns 0 for success.
- */
-static int
-init_output_merge(t_topology *top, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
- int i, j;
-
- init_output_common(top, out, data);
- for (i = 0; i < d->p2.nr; ++i)
- {
- for (j = 0; j < d->stride; ++j)
- {
- gmx_ana_pos_append_init(out->u.p, &d->g, &d->p1, d->stride*i+j);
- }
- gmx_ana_pos_append_init(out->u.p, &d->g, &d->p2, i);
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology data structure.
- * \param[in,out] out Pointer to output data structure.
- * \param[in,out] data Should point to \c t_methoddata_merge.
- * \returns 0 for success.
- */
-static int
-init_output_plus(t_topology *top, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
- int i;
-
- init_output_common(top, out, data);
- for (i = 0; i < d->p1.nr; ++i)
- {
- gmx_ana_pos_append_init(out->u.p, &d->g, &d->p1, i);
- }
- for (i = 0; i < d->p2.nr; ++i)
- {
- gmx_ana_pos_append_init(out->u.p, &d->g, &d->p2, i);
- }
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \p t_methoddata_merge).
- *
- * Frees the memory allocated for \c t_methoddata_merge.
- */
-static void
-free_data_merge(void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
-
- gmx_ana_index_deinit(&d->g);
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Not used.
- * \param[in] pbc Not used.
- * \param[in] p Positions to merge (should point to \p data->p1).
- * \param[out] out Output data structure (\p out->u.p is used).
- * \param[in] data Should point to a \p t_methoddata_merge.
- * \returns 0 on success.
- */
-static int
-evaluate_merge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
- int i, j;
- int refid;
-
- if (d->p1.nr != d->stride*d->p2.nr)
- {
- fprintf(stderr, "error: the number of positions to be merged are not compatible\n");
- return -1;
- }
- d->g.isize = 0;
- gmx_ana_pos_empty(out->u.p);
- for (i = 0; i < d->p2.nr; ++i)
- {
- for (j = 0; j < d->stride; ++j)
- {
- refid = d->p1.m.refid[d->stride*i+j];
- if (refid != -1)
- {
- refid = (d->stride+1) * (refid / d->stride) + (refid % d->stride);
- }
- gmx_ana_pos_append(out->u.p, &d->g, &d->p1, d->stride*i+j, refid);
- }
- refid = (d->stride+1)*d->p2.m.refid[i]+d->stride;
- gmx_ana_pos_append(out->u.p, &d->g, &d->p2, i, refid);
- }
- gmx_ana_pos_append_finish(out->u.p);
- return 0;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Not used.
- * \param[in] pbc Not used.
- * \param[in] p Positions to merge (should point to \p data->p1).
- * \param[out] out Output data structure (\p out->u.p is used).
- * \param[in] data Should point to a \p t_methoddata_merge.
- * \returns 0 on success.
- */
-static int
-evaluate_plus(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_merge *d = (t_methoddata_merge *)data;
- int i;
- int refid;
-
- d->g.isize = 0;
- gmx_ana_pos_empty(out->u.p);
- for (i = 0; i < d->p1.nr; ++i)
- {
- refid = d->p1.m.refid[i];
- gmx_ana_pos_append(out->u.p, &d->g, &d->p1, i, refid);
- }
- for (i = 0; i < d->p2.nr; ++i)
- {
- refid = d->p2.m.refid[i];
- if (refid != -1)
- {
- refid += d->p1.m.b.nr;
- }
- gmx_ana_pos_append(out->u.p, &d->g, &d->p2, i, refid);
- }
- gmx_ana_pos_append_finish(out->u.p);
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of the \p permute selection modifier.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <macros.h>
-#include <smalloc.h>
-#include <vec.h>
-
-#include <position.h>
-#include <selmethod.h>
-
-/*! \internal \brief
- * Data structure for the \p permute selection modifier.
- */
-typedef struct
-{
- /** Positions to permute. */
- gmx_ana_pos_t p;
- /** Group to receive the output permutation. */
- gmx_ana_index_t g;
- /** Number of elements in the permutation. */
- int n;
- /** Array describing the permutation. */
- int *perm;
- /** Array that has the permutation reversed. */
- int *rperm;
-} t_methoddata_permute;
-
-/** Allocates data for the \p permute selection modifier. */
-static void *
-init_data_permute(int npar, gmx_ana_selparam_t *param);
-/** Initializes data for the \p permute selection modifier. */
-static int
-init_permute(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes output for the \p permute selection modifier. */
-static int
-init_output_permute(t_topology *top, gmx_ana_selvalue_t *out, void *data);
-/** Frees the memory allocated for the \p permute selection modifier. */
-static void
-free_data_permute(void *data);
-/** Evaluates the \p permute selection modifier. */
-static int
-evaluate_permute(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data);
-
-/** Parameters for the \p permute selection modifier. */
-static gmx_ana_selparam_t smparams_permute[] = {
- {NULL, {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
- {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
-};
-
-/** Help text for the \p permute selection modifier. */
-static const char *help_permute[] = {
- "PERMUTING SELECTIONS[PAR]",
-
- "[TT]permute P1 ... PN[tt][PAR]",
-
- "By default, all selections are evaluated such that the atom indices are",
- "returned in ascending order. This can be changed by appending",
- "[TT]permute P1 P2 ... PN[tt] to an expression.",
- "The [TT]Pi[tt] should form a permutation of the numbers 1 to N.",
- "This keyword permutes each N-position block in the selection such that",
- "the i'th position in the block becomes Pi'th.",
- "Note that it is the positions that are permuted, not individual atoms.",
- "A fatal error occurs if the size of the selection is not a multiple of n.",
- "It is only possible to permute the whole selection expression, not any",
- "subexpressions, i.e., the [TT]permute[tt] keyword should appear last in",
- "a selection.",
-};
-
-/** \internal Selection method data for the \p permute modifier. */
-gmx_ana_selmethod_t sm_permute = {
- "permute", POS_VALUE, SMETH_MODIFIER,
- asize(smparams_permute), smparams_permute,
- &init_data_permute,
- NULL,
- &init_permute,
- &init_output_permute,
- &free_data_permute,
- NULL,
- NULL,
- &evaluate_permute,
- {"permute P1 ... PN", asize(help_permute), help_permute},
-};
-
-/*!
- * \param[in] npar Not used (should be 2).
- * \param[in,out] param Method parameters (should point to a copy of
- * \ref smparams_permute).
- * \returns Pointer to the allocated data (\p t_methoddata_permute).
- *
- * Allocates memory for a \p t_methoddata_permute structure.
- */
-static void *
-init_data_permute(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_permute *data;
-
- snew(data, 1);
- param[0].val.u.p = &data->p;
- return data;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used (should be 2).
- * \param[in] param Method parameters (should point to \ref smparams_permute).
- * \param[in] data Should point to a \p t_methoddata_permute.
- * \returns 0 if the input permutation is valid, -1 on error.
- */
-static int
-init_permute(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_permute *d = (t_methoddata_permute *)data;
- int i;
-
- gmx_ana_index_reserve(&d->g, d->p.g->isize);
- d->n = param[1].val.nr;
- d->perm = param[1].val.u.i;
- if (d->p.nr % d->n != 0)
- {
- fprintf(stderr, "error: the number of positions to be permuted is not divisible by %d\n",
- d->n);
- return -1;
- }
- snew(d->rperm, d->n);
- for (i = 0; i < d->n; ++i)
- {
- d->rperm[i] = -1;
- }
- for (i = 0; i < d->n; ++i)
- {
- d->perm[i]--;
- if (d->perm[i] < 0 || d->perm[i] >= d->n)
- {
- fprintf(stderr, "invalid permutation");
- return -1;
- }
- if (d->rperm[d->perm[i]] >= 0)
- {
- fprintf(stderr, "invalid permutation");
- return -1;
- }
- d->rperm[d->perm[i]] = i;
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology data structure.
- * \param[in,out] out Pointer to output data structure.
- * \param[in,out] data Should point to \c t_methoddata_permute.
- * \returns 0 for success.
- */
-static int
-init_output_permute(t_topology *top, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_permute *d = (t_methoddata_permute *)data;
- int i, j, b, k;
-
- gmx_ana_pos_copy(out->u.p, &d->p, TRUE);
- gmx_ana_pos_set_evalgrp(out->u.p, &d->g);
- d->g.isize = 0;
- gmx_ana_pos_empty_init(out->u.p);
- for (i = 0; i < d->p.nr; i += d->n)
- {
- for (j = 0; j < d->n; ++j)
- {
- b = i + d->rperm[j];
- gmx_ana_pos_append_init(out->u.p, &d->g, &d->p, b);
- }
- }
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \p t_methoddata_permute).
- *
- * Frees the memory allocated for \c t_methoddata_permute.
- */
-static void
-free_data_permute(void *data)
-{
- t_methoddata_permute *d = (t_methoddata_permute *)data;
-
- gmx_ana_index_deinit(&d->g);
- sfree(d->rperm);
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Not used.
- * \param[in] pbc Not used.
- * \param[in] p Positions to permute (should point to \p data->p).
- * \param[out] out Output data structure (\p out->u.p is used).
- * \param[in] data Should point to a \p t_methoddata_permute.
- * \returns 0 if \p p could be permuted, -1 on error.
- *
- * Returns -1 if the size of \p p is not divisible by the number of
- * elements in the permutation.
- */
-static int
-evaluate_permute(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_permute *d = (t_methoddata_permute *)data;
- int i, j, b, k;
- int refid;
-
- if (d->p.nr % d->n != 0)
- {
- fprintf(stderr, "error: the number of positions to be permuted is not divisible by %d\n",
- d->n);
- return -1;
- }
- d->g.isize = 0;
- gmx_ana_pos_empty(out->u.p);
- for (i = 0; i < d->p.nr; i += d->n)
- {
- for (j = 0; j < d->n; ++j)
- {
- b = i + d->rperm[j];
- refid = d->p.m.refid[b];
- if (refid != -1)
- {
- /* De-permute the reference ID */
- refid = refid - (refid % d->n) + d->perm[refid % d->n];
- }
- gmx_ana_pos_append(out->u.p, &d->g, p, b, refid);
- }
- }
- gmx_ana_pos_append_finish(out->u.p);
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of position evaluation selection methods.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <macros.h>
-#include <smalloc.h>
-#include <string2.h>
-
-#include <indexutil.h>
-#include <poscalc.h>
-#include <position.h>
-#include <selmethod.h>
-
-#include "keywords.h"
-#include "selelem.h"
-
-/*! \internal \brief
- * Data structure for position keyword evaluation.
- */
-typedef struct
-{
- /** Position calculation collection to use. */
- gmx_ana_poscalc_coll_t *pcc;
- /** Index group for which the center should be evaluated. */
- gmx_ana_index_t g;
- /** Position evaluation data structure. */
- gmx_ana_poscalc_t *pc;
- /** TRUE if periodic boundary conditions should be used. */
- gmx_bool bPBC;
- /** Type of positions to calculate. */
- char *type;
- /** Flags for the position calculation. */
- int flags;
-} t_methoddata_pos;
-
-/** Allocates data for position evaluation selection methods. */
-static void *
-init_data_pos(int npar, gmx_ana_selparam_t *param);
-/** Sets the position calculation collection for position evaluation selection methods. */
-static void
-set_poscoll_pos(gmx_ana_poscalc_coll_t *pcc, void *data);
-/** Initializes position evaluation keywords. */
-static int
-init_kwpos(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes the \p cog selection method. */
-static int
-init_cog(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes the \p cog selection method. */
-static int
-init_com(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Initializes output for position evaluation selection methods. */
-static int
-init_output_pos(t_topology *top, gmx_ana_selvalue_t *out, void *data);
-/** Frees the data allocated for position evaluation selection methods. */
-static void
-free_data_pos(void *data);
-/** Evaluates position evaluation selection methods. */
-static int
-evaluate_pos(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-
-/** Parameters for position keyword evaluation. */
-static gmx_ana_selparam_t smparams_keyword_pos[] = {
- {NULL, {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
-};
-
-/** Parameters for the \p cog and \p com selection methods. */
-static gmx_ana_selparam_t smparams_com[] = {
- {"of", {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
- {"pbc", {NO_VALUE, 0, {NULL}}, NULL, 0},
-};
-
-/** \internal Selection method data for position keyword evaluation. */
-gmx_ana_selmethod_t sm_keyword_pos = {
- "kw_pos", POS_VALUE, SMETH_DYNAMIC | SMETH_VARNUMVAL,
- asize(smparams_keyword_pos), smparams_keyword_pos,
- &init_data_pos,
- &set_poscoll_pos,
- &init_kwpos,
- &init_output_pos,
- &free_data_pos,
- NULL,
- &evaluate_pos,
- NULL,
- {NULL, 0, NULL},
-};
-
-/** \internal Selection method data for the \p cog method. */
-gmx_ana_selmethod_t sm_cog = {
- "cog", POS_VALUE, SMETH_DYNAMIC | SMETH_SINGLEVAL,
- asize(smparams_com), smparams_com,
- &init_data_pos,
- &set_poscoll_pos,
- &init_cog,
- &init_output_pos,
- &free_data_pos,
- NULL,
- &evaluate_pos,
- NULL,
- {"cog of ATOM_EXPR [pbc]", 0, NULL},
-};
-
-/** \internal Selection method data for the \p com method. */
-gmx_ana_selmethod_t sm_com = {
- "com", POS_VALUE, SMETH_REQTOP | SMETH_DYNAMIC | SMETH_SINGLEVAL,
- asize(smparams_com), smparams_com,
- &init_data_pos,
- &set_poscoll_pos,
- &init_com,
- &init_output_pos,
- &free_data_pos,
- NULL,
- &evaluate_pos,
- NULL,
- {"com of ATOM_EXPR [pbc]", 0, NULL},
-};
-
-/*!
- * \param[in] npar Should be 1 or 2.
- * \param[in,out] param Method parameters (should point to
- * \ref smparams_keyword_pos or \ref smparams_com).
- * \returns Pointer to the allocated data (\c t_methoddata_pos).
- *
- * Allocates memory for a \c t_methoddata_pos structure and initializes
- * the first parameter to define the value for \c t_methoddata_pos::g.
- * If a second parameter is present, it is used for setting the
- * \c t_methoddata_pos::bPBC flag.
- */
-static void *
-init_data_pos(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_pos *data;
-
- snew(data, 1);
- param[0].val.u.g = &data->g;
- if (npar > 1)
- {
- param[1].val.u.b = &data->bPBC;
- }
- data->pc = NULL;
- data->bPBC = FALSE;
- data->type = NULL;
- data->flags = -1;
- return data;
-}
-
-/*!
- * \param[in] pcc Position calculation collection to use.
- * \param[in,out] data Should point to \c t_methoddata_pos.
- */
-static void
-set_poscoll_pos(gmx_ana_poscalc_coll_t *pcc, void *data)
-{
- ((t_methoddata_pos *)data)->pcc = pcc;
-}
-
-/*!
- * \param[in,out] sel Selection element to initialize.
- * \param[in] type One of the enum values acceptable for
- * gmx_ana_poscalc_type_from_enum().
- *
- * Initializes the reference position type for position evaluation.
- * If called multiple times, the first setting takes effect, and later calls
- * are neglected.
- */
-void
-_gmx_selelem_set_kwpos_type(t_selelem *sel, const char *type)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)sel->u.expr.mdata;
-
- if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
- || sel->u.expr.method->name != sm_keyword_pos.name)
- {
- return;
- }
- if (!d->type && type)
- {
- d->type = strdup(type);
- /* FIXME: It would be better not to have the string here hardcoded. */
- if (type[0] != 'a')
- {
- sel->u.expr.method->flags |= SMETH_REQTOP;
- }
- }
-}
-
-/*!
- * \param[in,out] sel Selection element to initialize.
- * \param[in] flags Default completion flags
- * (see gmx_ana_poscalc_type_from_enum()).
- *
- * Initializes the flags for position evaluation.
- * If called multiple times, the first setting takes effect, and later calls
- * are neglected.
- */
-void
-_gmx_selelem_set_kwpos_flags(t_selelem *sel, int flags)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)sel->u.expr.mdata;
-
- if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
- || sel->u.expr.method->name != sm_keyword_pos.name)
- {
- return;
- }
- if (d->flags == -1)
- {
- d->flags = flags;
- }
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] npar Not used.
- * \param[in] param Not used.
- * \param[in,out] data Should point to \c t_methoddata_pos.
- * \returns 0 on success, a non-zero error code on error.
- *
- * The \c t_methoddata_pos::type field should have been initialized
- * externally using _gmx_selelem_set_kwpos_type().
- */
-static int
-init_kwpos(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)data;
- int rc;
-
- if (!(param[0].flags & SPAR_DYNAMIC))
- {
- d->flags &= ~(POS_DYNAMIC | POS_MASKONLY);
- }
- else if (!(d->flags & POS_MASKONLY))
- {
- d->flags |= POS_DYNAMIC;
- }
- rc = gmx_ana_poscalc_create_enum(&d->pc, d->pcc, d->type, d->flags);
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_poscalc_set_maxindex(d->pc, &d->g);
- return 0;
-}
-
-/*!
- * \param[in] top Topology data structure.
- * \param[in] npar Not used.
- * \param[in] param Not used.
- * \param[in,out] data Should point to \c t_methoddata_pos.
- * \returns 0 on success, a non-zero error code on error.
- */
-static int
-init_cog(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)data;
- int rc;
-
- d->flags = (param[0].flags & SPAR_DYNAMIC) ? POS_DYNAMIC : 0;
- rc = gmx_ana_poscalc_create(&d->pc, d->pcc, d->bPBC ? POS_ALL_PBC : POS_ALL,
- d->flags);
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_poscalc_set_maxindex(d->pc, &d->g);
- return 0;
-}
-
-/*!
- * \param[in] top Topology data structure.
- * \param[in] npar Not used.
- * \param[in] param Not used.
- * \param[in,out] data Should point to \c t_methoddata_pos.
- * \returns 0 on success, a non-zero error code on error.
- */
-static int
-init_com(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)data;
- int rc;
-
- d->flags = (param[0].flags & SPAR_DYNAMIC) ? POS_DYNAMIC : 0;
- d->flags |= POS_MASS;
- rc = gmx_ana_poscalc_create(&d->pc, d->pcc, d->bPBC ? POS_ALL_PBC : POS_ALL,
- d->flags);
- if (rc != 0)
- {
- return rc;
- }
- gmx_ana_poscalc_set_maxindex(d->pc, &d->g);
- return 0;
-}
-
-/*!
- * \param[in] top Topology data structure.
- * \param[in,out] out Pointer to output data structure.
- * \param[in,out] data Should point to \c t_methoddata_pos.
- * \returns 0 for success.
- */
-static int
-init_output_pos(t_topology *top, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)data;
-
- gmx_ana_poscalc_init_pos(d->pc, out->u.p);
- gmx_ana_pos_set_evalgrp(out->u.p, &d->g);
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \c t_methoddata_pos).
- *
- * Frees the memory allocated for \c t_methoddata_pos::g and
- * \c t_methoddata_pos::pc.
- */
-static void
-free_data_pos(void *data)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)data;
-
- sfree(d->type);
- gmx_ana_poscalc_free(d->pc);
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_pos.
- *
- * Calculates the positions using \c t_methoddata_pos::pc for the index group
- * in \c t_methoddata_pos::g and stores the results in \p out->u.p.
- */
-static int
-evaluate_pos(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_pos *d = (t_methoddata_pos *)data;
-
- gmx_ana_poscalc_update(d->pc, out->u.p, &d->g, fr, pbc);
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of the \p same selection method.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-
-#include <macros.h>
-#include <smalloc.h>
-#include <string2.h>
-
-#include <selmethod.h>
-
-#include "keywords.h"
-#include "parsetree.h"
-#include "selelem.h"
-
-/*! \internal \brief
- * Data structure for the \p same selection method.
- *
- * To avoid duplicate initialization code, the same data structure is used
- * for matching both integer and string keywords; hence the unions.
- */
-typedef struct
-{
- /** Value for each atom to match. */
- union
- {
- int *i;
- char **s;
- void *ptr;
- } val;
- /*! \brief
- * Number of values in the \p as array.
- *
- * For string values, this is actually the number of values in the
- * \p as_s_sorted array.
- */
- int nas;
- /** Values to match against. */
- union
- {
- int *i;
- char **s;
- void *ptr;
- } as;
- /*! \brief
- * Separate array for sorted \p as.s array.
- *
- * The array of strings returned as the output value of a parameter should
- * not be messed with to avoid memory corruption (the pointers in the array
- * may be reused for several evaluations), so we keep our own copy for
- * modifications.
- */
- char **as_s_sorted;
- /** Whether simple matching can be used. */
- gmx_bool bSorted;
-} t_methoddata_same;
-
-/** Allocates data for the \p same selection method. */
-static void *
-init_data_same(int npar, gmx_ana_selparam_t *param);
-/** Initializes the \p same selection method. */
-static int
-init_same(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Frees the data allocated for the \p same selection method. */
-static void
-free_data_same(void *data);
-/** Initializes the evaluation of the \p same selection method for a frame. */
-static int
-init_frame_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
-/** Evaluates the \p same selection method. */
-static int
-evaluate_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Initializes the evaluation of the \p same selection method for a frame. */
-static int
-init_frame_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
-/** Evaluates the \p same selection method. */
-static int
-evaluate_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-
-/** Parameters for the \p same selection method. */
-static gmx_ana_selparam_t smparams_same_int[] = {
- {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_ATOMVAL},
- {"as", {INT_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
-};
-
-/** Parameters for the \p same selection method. */
-static gmx_ana_selparam_t smparams_same_str[] = {
- {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_ATOMVAL},
- {"as", {STR_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
-};
-
-/** Help text for the \p same selection method. */
-static const char *help_same[] = {
- "EXTENDING SELECTIONS[PAR]",
-
- "[TT]same KEYWORD as ATOM_EXPR[tt][PAR]",
-
- "The keyword [TT]same[tt] can be used to select all atoms for which",
- "the given [TT]KEYWORD[tt] matches any of the atoms in [TT]ATOM_EXPR[tt].",
- "Keywords that evaluate to integer or string values are supported.",
-};
-
-/*! \internal \brief Selection method data for the \p same method. */
-gmx_ana_selmethod_t sm_same = {
- "same", GROUP_VALUE, 0,
- asize(smparams_same_int), smparams_same_int,
- &init_data_same,
- NULL,
- &init_same,
- NULL,
- &free_data_same,
- &init_frame_same_int,
- &evaluate_same_int,
- NULL,
- {"same KEYWORD as ATOM_EXPR", asize(help_same), help_same},
-};
-
-/*! \brief
- * Selection method data for the \p same method.
- *
- * This selection method is used for matching string keywords. The parser
- * never sees this method; _gmx_selelem_custom_init_same() replaces sm_same
- * with this method in cases where it is required.
- */
-static gmx_ana_selmethod_t sm_same_str = {
- "same", GROUP_VALUE, SMETH_SINGLEVAL,
- asize(smparams_same_str), smparams_same_str,
- &init_data_same,
- NULL,
- &init_same,
- NULL,
- &free_data_same,
- &init_frame_same_str,
- &evaluate_same_str,
- NULL,
- {"same KEYWORD as ATOM_EXPR", asize(help_same), help_same},
-};
-
-/*!
- * \param[in] npar Not used (should be 2).
- * \param[in,out] param Method parameters (should point to
- * \ref smparams_same).
- * \returns Pointer to the allocated data (\ref t_methoddata_same).
- */
-static void *
-init_data_same(int npar, gmx_ana_selparam_t *param)
-{
- t_methoddata_same *data;
-
- snew(data, 1);
- data->as_s_sorted = NULL;
- param[1].nvalptr = &data->nas;
- return data;
-}
-
-/*!
- * \param[in,out] method The method to initialize.
- * \param[in,out] params Pointer to the first parameter.
- * \param[in] scanner Scanner data structure.
- * \returns 0 on success, a non-zero error code on error.
- *
- * If \p *method is not a \c same method, this function returns zero
- * immediately.
- */
-int
-_gmx_selelem_custom_init_same(gmx_ana_selmethod_t **method,
- t_selexpr_param *params,
- void *scanner)
-{
- gmx_ana_selmethod_t *kwmethod;
- t_selelem *kwelem;
- t_selexpr_param *param;
- char *pname;
- int rc;
-
- /* Do nothing if this is not a same method. */
- if (!*method || (*method)->name != sm_same.name)
- {
- return 0;
- }
-
- if (params->nval != 1 || !params->value->bExpr
- || params->value->u.expr->type != SEL_EXPRESSION)
- {
- _gmx_selparser_error("error: 'same' should be followed by a single keyword");
- return -1;
- }
- kwmethod = params->value->u.expr->u.expr.method;
-
- if (kwmethod->type == STR_VALUE)
- {
- *method = &sm_same_str;
- }
-
- /* We do custom processing with the second parameter, so remove it from
- * the params list, but save the name for later. */
- param = params->next;
- params->next = NULL;
- pname = param->name;
- param->name = NULL;
- /* Create a second keyword evaluation element for the keyword given as
- * the first parameter, evaluating the keyword in the group given by the
- * second parameter. */
- rc = _gmx_sel_init_keyword_evaluator(&kwelem, kwmethod, param, scanner);
- if (rc != 0)
- {
- sfree(pname);
- return rc;
- }
- /* Replace the second parameter with one with a value from \p kwelem. */
- param = _gmx_selexpr_create_param(pname);
- param->nval = 1;
- param->value = _gmx_selexpr_create_value_expr(kwelem);
- params->next = param;
- return 0;
-}
-
-/*!
- * \param top Not used.
- * \param npar Not used (should be 2).
- * \param param Initialized method parameters (should point to a copy of
- * \ref smparams_same).
- * \param data Pointer to \ref t_methoddata_same to initialize.
- * \returns 0 on success, -1 on failure.
- */
-static int
-init_same(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- t_methoddata_same *d = (t_methoddata_same *)data;
-
- d->val.ptr = param[0].val.u.ptr;
- d->as.ptr = param[1].val.u.ptr;
- if (param[1].val.type == STR_VALUE)
- {
- snew(d->as_s_sorted, d->nas);
- }
- if (!(param[0].flags & SPAR_ATOMVAL))
- {
- fprintf(stderr, "ERROR: the same selection keyword combined with a "
- "non-keyword does not make sense\n");
- return -1;
- }
- return 0;
-}
-
-/*!
- * \param data Data to free (should point to a \ref t_methoddata_same).
- */
-static void
-free_data_same(void *data)
-{
- t_methoddata_same *d = (t_methoddata_same *)data;
-
- sfree(d->as_s_sorted);
-}
-
-/*! \brief
- * Helper function for comparison of two integers.
- */
-static int
-cmp_int(const void *a, const void *b)
-{
- if (*(int *)a < *(int *)b)
- {
- return -1;
- }
- if (*(int *)a > *(int *)b)
- {
- return 1;
- }
- return 0;
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Current frame.
- * \param[in] pbc PBC structure.
- * \param data Should point to a \ref t_methoddata_same.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Sorts the \c data->as.i array and removes identical values for faster and
- * simpler lookup.
- */
-static int
-init_frame_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
-{
- t_methoddata_same *d = (t_methoddata_same *)data;
- int i, j;
-
- /* Collapse adjacent values, and check whether the array is sorted. */
- d->bSorted = TRUE;
- for (i = 1, j = 0; i < d->nas; ++i)
- {
- if (d->as.i[i] != d->as.i[j])
- {
- if (d->as.i[i] < d->as.i[j])
- {
- d->bSorted = FALSE;
- }
- ++j;
- d->as.i[j] = d->as.i[i];
- }
- }
- d->nas = j + 1;
-
- if (!d->bSorted)
- {
- qsort(d->as.i, d->nas, sizeof(d->as.i[0]), &cmp_int);
- /* More identical values may become adjacent after sorting. */
- for (i = 1, j = 0; i < d->nas; ++i)
- {
- if (d->as.i[i] != d->as.i[j])
- {
- ++j;
- d->as.i[j] = d->as.i[i];
- }
- }
- d->nas = j + 1;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_same.
- *
- * Calculates which values in \c data->val.i can be found in \c data->as.i
- * (assumed sorted), and writes the corresponding atoms to output.
- * If \c data->val is sorted, uses a linear scan of both arrays, otherwise a
- * binary search of \c data->as is performed for each block of values in
- * \c data->val.
- */
-static int
-evaluate_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_same *d = (t_methoddata_same *)data;
- int i, j;
-
- out->u.g->isize = 0;
- i = j = 0;
- while (j < g->isize)
- {
- if (d->bSorted)
- {
- /* If we are sorted, we can do a simple linear scan. */
- while (i < d->nas && d->as.i[i] < d->val.i[j]) ++i;
- }
- else
- {
- /* If not, we must do a binary search of all the values. */
- int i1, i2;
-
- i1 = 0;
- i2 = d->nas;
- while (i2 - i1 > 1)
- {
- int itry = (i1 + i2) / 2;
- if (d->as.i[itry] <= d->val.i[j])
- {
- i1 = itry;
- }
- else
- {
- i2 = itry;
- }
- }
- i = (d->as.i[i1] == d->val.i[j] ? i1 : d->nas);
- }
- /* Check whether the value was found in the as list. */
- if (i == d->nas || d->as.i[i] != d->val.i[j])
- {
- /* If not, skip all atoms with the same value. */
- int tmpval = d->val.i[j];
- ++j;
- while (j < g->isize && d->val.i[j] == tmpval) ++j;
- }
- else
- {
- /* Copy all the atoms with this value to the output. */
- while (j < g->isize && d->val.i[j] == d->as.i[i])
- {
- out->u.g->index[out->u.g->isize++] = g->index[j];
- ++j;
- }
- }
- if (j < g->isize && d->val.i[j] < d->val.i[j - 1])
- {
- d->bSorted = FALSE;
- }
- }
- return 0;
-}
-
-/*! \brief
- * Helper function for comparison of two strings.
- */
-static int
-cmp_str(const void *a, const void *b)
-{
- return strcmp(*(char **)a, *(char **)b);
-}
-
-/*!
- * \param[in] top Not used.
- * \param[in] fr Current frame.
- * \param[in] pbc PBC structure.
- * \param data Should point to a \ref t_methoddata_same.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Sorts the \c data->as.s array and removes identical values for faster and
- * simpler lookup.
- */
-static int
-init_frame_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
-{
- t_methoddata_same *d = (t_methoddata_same *)data;
- int i, j;
-
- /* Collapse adjacent values.
- * For strings, it's unlikely that the values would be sorted originally,
- * so set bSorted always to FALSE. */
- d->bSorted = FALSE;
- d->as_s_sorted[0] = d->as.s[0];
- for (i = 1, j = 0; i < d->nas; ++i)
- {
- if (strcmp(d->as.s[i], d->as_s_sorted[j]) != 0)
- {
- ++j;
- d->as_s_sorted[j] = d->as.s[i];
- }
- }
- d->nas = j + 1;
-
- qsort(d->as_s_sorted, d->nas, sizeof(d->as_s_sorted[0]), &cmp_str);
- /* More identical values may become adjacent after sorting. */
- for (i = 1, j = 0; i < d->nas; ++i)
- {
- if (strcmp(d->as_s_sorted[i], d->as_s_sorted[j]) != 0)
- {
- ++j;
- d->as_s_sorted[j] = d->as_s_sorted[i];
- }
- }
- d->nas = j + 1;
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data should point to a \c t_methoddata_same.
- *
- * Calculates which strings in \c data->val.s can be found in \c data->as.s
- * (assumed sorted), and writes the corresponding atoms to output.
- * A binary search of \c data->as is performed for each block of values in
- * \c data->val.
- */
-static int
-evaluate_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- t_methoddata_same *d = (t_methoddata_same *)data;
- int i, j;
-
- out->u.g->isize = 0;
- j = 0;
- while (j < g->isize)
- {
- /* Do a binary search of the strings. */
- void *ptr;
- ptr = bsearch(&d->val.s[j], d->as_s_sorted, d->nas,
- sizeof(d->as_s_sorted[0]), &cmp_str);
- /* Check whether the value was found in the as list. */
- if (ptr == NULL)
- {
- /* If not, skip all atoms with the same value. */
- const char *tmpval = d->val.s[j];
- ++j;
- while (j < g->isize && strcmp(d->val.s[j], tmpval) == 0) ++j;
- }
- else
- {
- const char *tmpval = d->val.s[j];
- /* Copy all the atoms with this value to the output. */
- while (j < g->isize && strcmp(d->val.s[j], tmpval) == 0)
- {
- out->u.g->index[out->u.g->isize++] = g->index[j];
- ++j;
- }
- }
- }
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementations of simple keyword selection methods.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <position.h>
-#include <selmethod.h>
-
-/** Evaluates the \p all selection keyword. */
-static int
-evaluate_all(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p none selection keyword. */
-static int
-evaluate_none(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p atomnr selection keyword. */
-static int
-evaluate_atomnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p resnr selection keyword. */
-static int
-evaluate_resnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p resindex selection keyword. */
-static int
-evaluate_resindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Checks whether molecule information is present in the topology. */
-static int
-check_molecules(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Evaluates the \p molindex selection keyword. */
-static int
-evaluate_molindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p name selection keyword. */
-static int
-evaluate_atomname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Checks whether atom types are present in the topology. */
-static int
-check_atomtype(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Evaluates the \p type selection keyword. */
-static int
-evaluate_atomtype(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p insertcode selection keyword. */
-static int
-evaluate_insertcode(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p chain selection keyword. */
-static int
-evaluate_chain(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p mass selection keyword. */
-static int
-evaluate_mass(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p charge selection keyword. */
-static int
-evaluate_charge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Checks whether PDB info is present in the topology. */
-static int
-check_pdbinfo(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
-/** Evaluates the \p altloc selection keyword. */
-static int
-evaluate_altloc(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p occupancy selection keyword. */
-static int
-evaluate_occupancy(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p betafactor selection keyword. */
-static int
-evaluate_betafactor(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p resname selection keyword. */
-static int
-evaluate_resname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
-
-/** Evaluates the \p x selection keyword. */
-static int
-evaluate_x(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p y selection keyword. */
-static int
-evaluate_y(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
-/** Evaluates the \p z selection keyword. */
-static int
-evaluate_z(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
-
-/** \internal Selection method data for \p all selection keyword. */
-gmx_ana_selmethod_t sm_all = {
- "all", GROUP_VALUE, 0,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_all,
- NULL,
-};
-
-/** \internal Selection method data for \p none selection keyword. */
-gmx_ana_selmethod_t sm_none = {
- "none", GROUP_VALUE, 0,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_none,
- NULL,
-};
-
-/** \internal Selection method data for \p atomnr selection keyword. */
-gmx_ana_selmethod_t sm_atomnr = {
- "atomnr", INT_VALUE, 0,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_atomnr,
- NULL,
-};
-
-/** \internal Selection method data for \p resnr selection keyword. */
-gmx_ana_selmethod_t sm_resnr = {
- "resnr", INT_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_resnr,
- NULL,
-};
-
-/** \internal Selection method data for \p resindex selection keyword. */
-gmx_ana_selmethod_t sm_resindex = {
- "resindex", INT_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_resindex,
- NULL,
-};
-
-/** \internal Selection method data for \p molindex selection keyword. */
-gmx_ana_selmethod_t sm_molindex = {
- "molindex", INT_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- &check_molecules,
- NULL,
- NULL,
- NULL,
- &evaluate_molindex,
- NULL,
-};
-
-/** \internal Selection method data for \p name selection keyword. */
-gmx_ana_selmethod_t sm_atomname = {
- "name", STR_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_atomname,
- NULL,
-};
-
-/** \internal Selection method data for \p type selection keyword. */
-gmx_ana_selmethod_t sm_atomtype = {
- "type", STR_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- &check_atomtype,
- NULL,
- NULL,
- NULL,
- &evaluate_atomtype,
- NULL,
-};
-
-/** \internal Selection method data for \p resname selection keyword. */
-gmx_ana_selmethod_t sm_resname = {
- "resname", STR_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_resname,
- NULL,
-};
-
-/** \internal Selection method data for \p chain selection keyword. */
-gmx_ana_selmethod_t sm_insertcode = {
- "insertcode", STR_VALUE, SMETH_REQTOP | SMETH_CHARVAL,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_insertcode,
- NULL,
-};
-
-/** \internal Selection method data for \p chain selection keyword. */
-gmx_ana_selmethod_t sm_chain = {
- "chain", STR_VALUE, SMETH_REQTOP | SMETH_CHARVAL,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_chain,
- NULL,
-};
-
-/** \internal Selection method data for \p mass selection keyword. */
-gmx_ana_selmethod_t sm_mass = {
- "mass", REAL_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_mass,
- NULL,
-};
-
-/** \internal Selection method data for \p charge selection keyword. */
-gmx_ana_selmethod_t sm_charge = {
- "charge", REAL_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_charge,
- NULL,
-};
-
-/** \internal Selection method data for \p chain selection keyword. */
-gmx_ana_selmethod_t sm_altloc = {
- "altloc", STR_VALUE, SMETH_REQTOP | SMETH_CHARVAL,
- 0, NULL,
- NULL,
- NULL,
- &check_pdbinfo,
- NULL,
- NULL,
- NULL,
- &evaluate_altloc,
- NULL,
-};
-
-/** \internal Selection method data for \p occupancy selection keyword. */
-gmx_ana_selmethod_t sm_occupancy = {
- "occupancy", REAL_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- &check_pdbinfo,
- NULL,
- NULL,
- NULL,
- &evaluate_occupancy,
- NULL,
-};
-
-/** \internal Selection method data for \p betafactor selection keyword. */
-gmx_ana_selmethod_t sm_betafactor = {
- "betafactor", REAL_VALUE, SMETH_REQTOP,
- 0, NULL,
- NULL,
- NULL,
- &check_pdbinfo,
- NULL,
- NULL,
- NULL,
- &evaluate_betafactor,
- NULL,
-};
-
-/** \internal Selection method data for \p x selection keyword. */
-gmx_ana_selmethod_t sm_x = {
- "x", REAL_VALUE, SMETH_DYNAMIC,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_x,
-};
-
-/** \internal Selection method data for \p y selection keyword. */
-gmx_ana_selmethod_t sm_y = {
- "y", REAL_VALUE, SMETH_DYNAMIC,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_y,
-};
-
-/** \internal Selection method data for \p z selection keyword. */
-gmx_ana_selmethod_t sm_z = {
- "z", REAL_VALUE, SMETH_DYNAMIC,
- 0, NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- &evaluate_z,
-};
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Copies \p g to \p out->u.g.
- */
-static int
-evaluate_all(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- gmx_ana_index_copy(out->u.g, g, FALSE);
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns an empty \p out->u.g.
- */
-static int
-evaluate_none(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- out->u.g->isize = 0;
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the indices for each atom in \p out->u.i.
- */
-static int
-evaluate_atomnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.i[i] = g->index[i] + 1;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the residue numbers for each atom in \p out->u.i.
- */
-static int
-evaluate_resnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
- int resind;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- resind = top->atoms.atom[g->index[i]].resind;
- out->u.i[i] = top->atoms.resinfo[resind].nr;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the residue indices for each atom in \p out->u.i.
- */
-static int
-evaluate_resindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.i[i] = top->atoms.atom[g->index[i]].resind + 1;
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure.
- * \param npar Not used.
- * \param param Not used.
- * \param data Not used.
- * \returns 0 if molecule info is present in the topology, -1 otherwise.
- *
- * If molecule information is not found, also prints an error message.
- */
-static int
-check_molecules(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- gmx_bool bOk;
-
- bOk = (top != NULL && top->mols.nr > 0);
- if (!bOk)
- {
- fprintf(stderr, "Molecule information not available in topology!\n");
- return -1;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the molecule indices for each atom in \p out->u.i.
- */
-static int
-evaluate_molindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i, j;
-
- out->nr = g->isize;
- for (i = j = 0; i < g->isize; ++i)
- {
- while (top->mols.index[j + 1] <= g->index[i]) ++j;
- out->u.i[i] = j + 1;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the atom name for each atom in \p out->u.s.
- */
-static int
-evaluate_atomname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.s[i] = *top->atoms.atomname[g->index[i]];
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure.
- * \param npar Not used.
- * \param param Not used.
- * \param data Not used.
- * \returns 0 if atom types are present in the topology, -1 otherwise.
- *
- * If the atom types are not found, also prints an error message.
- */
-static int
-check_atomtype(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- gmx_bool bOk;
-
- bOk = (top != NULL && top->atoms.atomtype != NULL);
- if (!bOk)
- {
- fprintf(stderr, "Atom types not available in topology!\n");
- return -1;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the atom type for each atom in \p out->u.s.
- * Segfaults if atom types are not found in the topology.
- */
-static int
-evaluate_atomtype(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.s[i] = *top->atoms.atomtype[g->index[i]];
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the residue name for each atom in \p out->u.s.
- */
-static int
-evaluate_resname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
- int resind;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- resind = top->atoms.atom[g->index[i]].resind;
- out->u.s[i] = *top->atoms.resinfo[resind].name;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the insertion code for each atom in \p out->u.s.
- */
-static int
-evaluate_insertcode(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
- int resind;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- resind = top->atoms.atom[g->index[i]].resind;
- out->u.s[i][0] = top->atoms.resinfo[resind].ic;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the chain for each atom in \p out->u.s.
- */
-static int
-evaluate_chain(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
- int resind;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- resind = top->atoms.atom[g->index[i]].resind;
- out->u.s[i][0] = top->atoms.resinfo[resind].chainid;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the mass for each atom in \p out->u.r.
- */
-static int
-evaluate_mass(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.r[i] = top->atoms.atom[g->index[i]].m;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the charge for each atom in \p out->u.r.
- */
-static int
-evaluate_charge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.r[i] = top->atoms.atom[g->index[i]].q;
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure.
- * \param npar Not used.
- * \param param Not used.
- * \param data Not used.
- * \returns 0 if PDB info is present in the topology, -1 otherwise.
- *
- * If PDB info is not found, also prints an error message.
- */
-static int
-check_pdbinfo(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
-{
- gmx_bool bOk;
-
- bOk = (top != NULL && top->atoms.pdbinfo != NULL);
- if (!bOk)
- {
- fprintf(stderr, "PDB info not available in topology!\n");
- return -1;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the alternate location identifier for each atom in \p out->u.s.
- */
-static int
-evaluate_altloc(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.s[i][0] = top->atoms.pdbinfo[g->index[i]].altloc;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the occupancy numbers for each atom in \p out->u.r.
- * Segfaults if PDB info is not found in the topology.
- */
-static int
-evaluate_occupancy(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.r[i] = top->atoms.pdbinfo[g->index[i]].occup;
- }
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the B-factors for each atom in \p out->u.r.
- * Segfaults if PDB info is not found in the topology.
- */
-static int
-evaluate_betafactor(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
-{
- int i;
-
- out->nr = g->isize;
- for (i = 0; i < g->isize; ++i)
- {
- out->u.r[i] = top->atoms.pdbinfo[g->index[i]].bfac;
- }
- return 0;
-}
-
-/*! \brief
- * Internal utility function for position keyword evaluation.
- *
- * \param[in] fr Current frame.
- * \param[in] g Index group for which the coordinates should be evaluated.
- * \param[out] out Output array.
- * \param[in] pos Position data to use instead of atomic coordinates
- * (can be NULL).
- * \param[in] d Coordinate index to evaluate (\p XX, \p YY or \p ZZ).
- *
- * This function is used internally by evaluate_x(), evaluate_y() and
- * evaluate_z() to do the actual evaluation.
- */
-static void
-evaluate_coord(t_trxframe *fr, gmx_ana_index_t *g, real out[],
- gmx_ana_pos_t *pos, int d)
-{
- int b, i;
- real v;
-
- if (pos)
- {
- for (b = 0; b < pos->nr; ++b)
- {
- v = pos->x[b][d];
- for (i = pos->m.mapb.index[b]; i < pos->m.mapb.index[b+1]; ++i)
- {
- out[i] = v;
- }
- }
- }
- else
- {
- for (i = 0; i < g->isize; ++i)
- {
- out[i] = fr->x[g->index[i]][d];
- }
- }
-}
-
-/*!
- * See sel_updatefunc_pos() for description of the parameters.
- * \p data is not used.
- *
- * Returns the \p x coordinate for each atom in \p out->u.r.
- */
-static int
-evaluate_x(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
-{
- out->nr = pos->g->isize;
- evaluate_coord(fr, pos->g, out->u.r, pos, XX);
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the \p y coordinate for each atom in \p out->u.r.
- */
-static int
-evaluate_y(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
-{
- out->nr = pos->g->isize;
- evaluate_coord(fr, pos->g, out->u.r, pos, YY);
- return 0;
-}
-
-/*!
- * See sel_updatefunc() for description of the parameters.
- * \p data is not used.
- *
- * Returns the \p z coordinate for each atom in \p out->u.r.
- */
-static int
-evaluate_z(t_topology *top, t_trxframe *fr, t_pbc *pbc,
- gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
-{
- out->nr = pos->g->isize;
- evaluate_coord(fr, pos->g, out->u.r, pos, ZZ);
- return 0;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in symrec.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <macros.h>
-#include <smalloc.h>
-#include <string2.h>
-#include <typedefs.h>
-#include <gmx_fatal.h>
-
-#include <poscalc.h>
-
-#include "selelem.h"
-#include "symrec.h"
-
-/*! \internal \brief
- * Symbol table for the selection parser.
- */
-struct gmx_sel_symtab_t
-{
- /** Pointer to the first symbol in the linked list of symbols. */
- gmx_sel_symrec_t *first;
-};
-
-/*! \internal \brief
- * Single symbol for the selection parser.
- */
-struct gmx_sel_symrec_t
-{
- /** Name of the symbol. */
- char *name;
- /** Type of the symbol. */
- e_symbol_t type;
- /** Value of the symbol. */
- union {
- /** Pointer to the method structure (\ref SYMBOL_METHOD). */
- struct gmx_ana_selmethod_t *meth;
- /** Pointer to the variable value (\ref SYMBOL_VARIABLE). */
- struct t_selelem *var;
- } u;
- /** Pointer to the next symbol. */
- struct gmx_sel_symrec_t *next;
-};
-
-/** List of reserved symbols to register in add_reserved_symbols(). */
-static const char *const sym_reserved[] = {
- "group",
- "to",
- "not",
- "and",
- "or",
- "xor",
- "yes",
- "no",
- "on",
- "off",
- "help",
-};
-
-/*!
- * \param[in] sym Symbol to query.
- * \returns The name of \p sym.
- *
- * The returned pointer should not be free'd.
- */
-char *
-_gmx_sel_sym_name(gmx_sel_symrec_t *sym)
-{
- return sym->name;
-}
-
-/*!
- * \param[in] sym Symbol to query.
- * \returns The type of \p sym.
- */
-e_symbol_t
-_gmx_sel_sym_type(gmx_sel_symrec_t *sym)
-{
- return sym->type;
-}
-
-/*!
- * \param[in] sym Symbol to query.
- * \returns The method associated with \p sym, or NULL if \p sym is not a
- * \ref SYMBOL_METHOD symbol.
- */
-struct gmx_ana_selmethod_t *
-_gmx_sel_sym_value_method(gmx_sel_symrec_t *sym)
-{
- if (sym->type != SYMBOL_METHOD)
- {
- gmx_call("symbol is not a method symbol");
- return NULL;
- }
- return sym->u.meth;
-}
-
-/*!
- * \param[in] sym Symbol to query.
- * \returns The variable expression associated with \p sym, or NULL if
- * \p sym is not a \ref SYMBOL_VARIABLE symbol.
- */
-struct t_selelem *
-_gmx_sel_sym_value_var(gmx_sel_symrec_t *sym)
-{
- if (sym->type != SYMBOL_VARIABLE)
- {
- gmx_call("symbol is not a variable symbol");
- return NULL;
- }
- return sym->u.var;
-}
-
-/*! \brief
- * Adds the reserved symbols to a symbol table.
- *
- * \param[in,out] tab Symbol table to which the symbols are added.
- *
- * Assumes that the symbol table is empty.
- */
-static void
-add_reserved_symbols(gmx_sel_symtab_t *tab)
-{
- gmx_sel_symrec_t *sym;
- gmx_sel_symrec_t *last;
- size_t i;
-
- last = NULL;
- for (i = 0; i < asize(sym_reserved); ++i)
- {
- snew(sym, 1);
- sym->name = strdup(sym_reserved[i]);
- sym->type = SYMBOL_RESERVED;
- sym->next = NULL;
- if (last)
- {
- last->next = sym;
- }
- else
- {
- tab->first = sym;
- }
- last = sym;
- }
-}
-
-/*! \brief
- * Adds the position symbols to the symbol list.
- *
- * \param[in,out] tab Symbol table to which the symbols are added.
- */
-static void
-add_position_symbols(gmx_sel_symtab_t *tab)
-{
- const char **postypes;
- gmx_sel_symrec_t *sym;
- gmx_sel_symrec_t *last;
- int i;
-
- postypes = gmx_ana_poscalc_create_type_enum(TRUE);
- last = tab->first;
- while (last && last->next)
- {
- last = last->next;
- }
- for (i = 1; postypes[i] != NULL; ++i)
- {
- snew(sym, 1);
- sym->name = strdup(postypes[i]);
- sym->type = SYMBOL_POS;
- sym->next = NULL;
- if (last)
- {
- last->next = sym;
- }
- else
- {
- tab->first = sym;
- }
- last = sym;
- }
- sfree(postypes);
-}
-
-/*!
- * \param[out] tabp Symbol table pointer to initialize.
- *
- * Reserved and position symbols are added to the created table.
- */
-int
-_gmx_sel_symtab_create(gmx_sel_symtab_t **tabp)
-{
- gmx_sel_symtab_t *tab;
-
- snew(tab, 1);
- add_reserved_symbols(tab);
- add_position_symbols(tab);
- *tabp = tab;
- return 0;
-}
-
-/*!
- * \param[in] tab Symbol table to free.
- *
- * The pointer \p tab is invalid after the call.
- */
-void
-_gmx_sel_symtab_free(gmx_sel_symtab_t *tab)
-{
- gmx_sel_symrec_t *sym;
-
- while (tab->first)
- {
- sym = tab->first;
- tab->first = sym->next;
- if (sym->type == SYMBOL_VARIABLE)
- {
- _gmx_selelem_free(sym->u.var);
- }
- sfree(sym->name);
- sfree(sym);
- }
- sfree(tab);
-}
-
-/*!
- * \param[in] tab Symbol table to search.
- * \param[in] name Symbol name to find.
- * \param[in] bExact If FALSE, symbols that begin with \p name are also
- * considered.
- * \returns Pointer to the symbol with name \p name, or NULL if not found.
- *
- * If no exact match is found and \p bExact is FALSE, returns a symbol that
- * begins with \p name if a unique matching symbol is found.
- */
-gmx_sel_symrec_t *
-_gmx_sel_find_symbol(gmx_sel_symtab_t *tab, const char *name, gmx_bool bExact)
-{
- return _gmx_sel_find_symbol_len(tab, name, strlen(name), bExact);
-}
-
-/*!
- * \param[in] tab Symbol table to search.
- * \param[in] name Symbol name to find.
- * \param[in] len Only consider the first \p len characters of \p name.
- * \param[in] bExact If FALSE, symbols that begin with \p name are also
- * considered.
- * \returns Pointer to the symbol with name \p name, or NULL if not found.
- *
- * If no exact match is found and \p bExact is FALSE, returns a symbol that
- * begins with \p name if a unique matching symbol is found.
- *
- * The parameter \p len is there to allow using this function from scanner.l
- * without modifying the text to be scanned or copying it.
- */
-gmx_sel_symrec_t *
-_gmx_sel_find_symbol_len(gmx_sel_symtab_t *tab, const char *name, size_t len,
- gmx_bool bExact)
-{
- gmx_sel_symrec_t *sym;
- gmx_sel_symrec_t *match;
- gmx_bool bUnique;
- gmx_bool bMatch;
-
- match = NULL;
- bUnique = TRUE;
- bMatch = FALSE;
- sym = tab->first;
- while (sym)
- {
- if (!strncmp(sym->name, name, len))
- {
- if (strlen(sym->name) == len)
- {
- return sym;
- }
- if (bMatch)
- {
- bUnique = FALSE;
- }
- bMatch = TRUE;
- if (sym->type == SYMBOL_METHOD)
- {
- match = sym;
- }
- }
- sym = sym->next;
- }
- if (bExact)
- {
- return NULL;
- }
-
- if (!bUnique)
- {
- fprintf(stderr, "parse error: ambiguous symbol\n");
- return NULL;
- }
- return match;
-}
-
-/*!
- * \param[in] tab Symbol table to search.
- * \param[in] type Type of symbol to find.
- * \returns The first symbol in \p tab with type \p type,
- * or NULL if there are no such symbols.
- */
-gmx_sel_symrec_t *
-_gmx_sel_first_symbol(gmx_sel_symtab_t *tab, e_symbol_t type)
-{
- gmx_sel_symrec_t *sym;
-
- sym = tab->first;
- while (sym)
- {
- if (sym->type == type)
- {
- return sym;
- }
- sym = sym->next;
- }
- return NULL;
-}
-
-/*!
- * \param[in] after Start the search after this symbol.
- * \param[in] type Type of symbol to find.
- * \returns The next symbol after \p after with type \p type,
- * or NULL if there are no more symbols.
- */
-gmx_sel_symrec_t *
-_gmx_sel_next_symbol(gmx_sel_symrec_t *after, e_symbol_t type)
-{
- gmx_sel_symrec_t *sym;
-
- sym = after->next;
- while (sym)
- {
- if (sym->type == type)
- {
- return sym;
- }
- sym = sym->next;
- }
- return NULL;
-}
-
-/*! \brief
- * Internal utility function used in adding symbols to a symbol table.
- *
- * \param[in,out] tab Symbol table to add the symbol to.
- * \param[in] name Name of the symbol to add.
- * \param[out] ctype On error, the type of the conflicting symbol is
- * written to \p *ctype.
- * \returns Pointer to the new symbol record, or NULL if \p name
- * conflicts with an existing symbol.
- */
-static gmx_sel_symrec_t *
-add_symbol(gmx_sel_symtab_t *tab, const char *name, e_symbol_t *ctype)
-{
- gmx_sel_symrec_t *sym, *psym;
- int len;
-
- /* Check if there is a conflicting symbol */
- psym = NULL;
- sym = tab->first;
- while (sym)
- {
- if (!gmx_strcasecmp(sym->name, name))
- {
- *ctype = sym->type;
- return NULL;
- }
- psym = sym;
- sym = sym->next;
- }
-
- /* Create a new symbol record */
- if (psym == NULL)
- {
- snew(tab->first, 1);
- sym = tab->first;
- }
- else
- {
- snew(psym->next, 1);
- sym = psym->next;
- }
- sym->name = strdup(name);
- return sym;
-}
-
-/*!
- * \param[in,out] tab Symbol table to add the symbol to.
- * \param[in] name Name of the new symbol.
- * \param[in] sel Value of the variable.
- * \returns Pointer to the created symbol record, or NULL if there was a
- * symbol with the same name.
- */
-gmx_sel_symrec_t *
-_gmx_sel_add_var_symbol(gmx_sel_symtab_t *tab, const char *name,
- struct t_selelem *sel)
-{
- gmx_sel_symrec_t *sym;
- e_symbol_t ctype;
-
- sym = add_symbol(tab, name, &ctype);
- if (!sym)
- {
- fprintf(stderr, "parse error: ");
- switch (ctype)
- {
- case SYMBOL_RESERVED:
- case SYMBOL_POS:
- fprintf(stderr, "variable name (%s) conflicts with a reserved keyword\n",
- name);
- break;
- case SYMBOL_VARIABLE:
- fprintf(stderr, "duplicate variable name (%s)\n", name);
- break;
- case SYMBOL_METHOD:
- fprintf(stderr, "variable name (%s) conflicts with a selection keyword\n",
- name);
- break;
- }
- return NULL;
- }
-
- sym->type = SYMBOL_VARIABLE;
- sym->u.var = sel;
- sel->refcount++;
- return sym;
-}
-
-/*!
- * \param[in,out] tab Symbol table to add the symbol to.
- * \param[in] name Name of the new symbol.
- * \param[in] method Method that this symbol represents.
- * \returns Pointer to the created symbol record, or NULL if there was a
- * symbol with the same name.
- */
-gmx_sel_symrec_t *
-_gmx_sel_add_method_symbol(gmx_sel_symtab_t *tab, const char *name,
- struct gmx_ana_selmethod_t *method)
-{
- gmx_sel_symrec_t *sym;
- e_symbol_t ctype;
-
- sym = add_symbol(tab, name, &ctype);
- if (!sym)
- {
- fprintf(stderr, "parse error: ");
- switch (ctype)
- {
- case SYMBOL_RESERVED:
- case SYMBOL_POS:
- fprintf(stderr, "method name (%s) conflicts with a reserved keyword\n",
- name);
- break;
- case SYMBOL_VARIABLE:
- fprintf(stderr, "method name (%s) conflicts with a variable name\n",
- name);
- break;
- case SYMBOL_METHOD:
- fprintf(stderr, "duplicate method name (%s)\n", name);
- break;
- }
- return NULL;
- }
-
- sym->type = SYMBOL_METHOD;
- sym->u.meth = method;
- return sym;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Handling of selection parser symbol table.
- *
- * This is an implementation header: there should be no need to use it outside
- * this directory.
- */
-#ifndef SELECTION_SYMREC_H
-#define SELECTION_SYMREC_H
-
-struct t_selelem;
-struct gmx_ana_selmethod_t;
-
-/** Defines the type of the symbol. */
-typedef enum
-{
- SYMBOL_RESERVED, /**< The symbol is a reserved keyword. */
- SYMBOL_VARIABLE, /**< The symbol is a variable. */
- SYMBOL_METHOD, /**< The symbol is a selection method. */
- SYMBOL_POS /**< The symbol is a position keyword. */
-} e_symbol_t;
-
-/** Symbol table for the selection parser. */
-typedef struct gmx_sel_symtab_t gmx_sel_symtab_t;
-/** Single symbol for the selection parser. */
-typedef struct gmx_sel_symrec_t gmx_sel_symrec_t;
-
-/** Returns the name of a symbol. */
-char *
-_gmx_sel_sym_name(gmx_sel_symrec_t *sym);
-/** Returns the type of a symbol. */
-e_symbol_t
-_gmx_sel_sym_type(gmx_sel_symrec_t *sym);
-/** Returns the method associated with a \ref SYMBOL_METHOD symbol. */
-struct gmx_ana_selmethod_t *
-_gmx_sel_sym_value_method(gmx_sel_symrec_t *sym);
-/** Returns the method associated with a \ref SYMBOL_VARIABLE symbol. */
-struct t_selelem *
-_gmx_sel_sym_value_var(gmx_sel_symrec_t *sym);
-
-/** Creates a new symbol table. */
-int
-_gmx_sel_symtab_create(gmx_sel_symtab_t **tabp);
-/** Frees all memory allocated for a symbol table. */
-void
-_gmx_sel_symtab_free(gmx_sel_symtab_t *tab);
-/** Finds a symbol by name. */
-gmx_sel_symrec_t *
-_gmx_sel_find_symbol(gmx_sel_symtab_t *tab, const char *name, gmx_bool bExact);
-/** Finds a symbol by name. */
-gmx_sel_symrec_t *
-_gmx_sel_find_symbol_len(gmx_sel_symtab_t *tab, const char *name, size_t len,
- gmx_bool bExact);
-/** Returns the first symbol of a given type. */
-gmx_sel_symrec_t *
-_gmx_sel_first_symbol(gmx_sel_symtab_t *tab, e_symbol_t type);
-/** Returns the next symbol of a given type. */
-gmx_sel_symrec_t *
-_gmx_sel_next_symbol(gmx_sel_symrec_t *after, e_symbol_t type);
-/** Adds a new variable symbol. */
-gmx_sel_symrec_t *
-_gmx_sel_add_var_symbol(gmx_sel_symtab_t *tab, const char *name,
- struct t_selelem *sel);
-/** Adds a new method symbol. */
-gmx_sel_symrec_t *
-_gmx_sel_add_method_symbol(gmx_sel_symtab_t *tab, const char *name,
- struct gmx_ana_selmethod_t *method);
-
-#endif
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Testing/debugging tool for the selection engine.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <copyrite.h>
-#include <filenm.h>
-#include <macros.h>
-#include <smalloc.h>
-#include <statutil.h>
-
-#include <trajana.h>
-
-typedef struct
-{
- gmx_bool bFrameTree;
- int nmaxind;
- gmx_ana_selcollection_t *sc;
-} t_dumpdata;
-
-int
-dump_frame(t_topology * top, t_trxframe * fr, t_pbc * pbc,
- int nr, gmx_ana_selection_t *sel[], void *data)
-{
- t_dumpdata *d = (t_dumpdata *)data;
- int g, i, n;
-
- fprintf(stderr, "\n");
- if (d->bFrameTree)
- {
- gmx_ana_selcollection_print_tree(stderr, d->sc, TRUE);
- }
- for (g = 0; g < nr; ++g)
- {
- gmx_ana_index_dump(sel[g]->g, g, d->nmaxind);
- fprintf(stderr, " Positions (%d pcs):\n", sel[g]->p.nr);
- n = sel[g]->p.nr;
- if (d->nmaxind >= 0 && n > d->nmaxind)
- {
- n = d->nmaxind;
- }
- for (i = 0; i < n; ++i)
- {
- fprintf(stderr, " (%.2f,%.2f,%.2f) r=%d, m=%d, b=%d-%d\n",
- sel[g]->p.x[i][XX], sel[g]->p.x[i][YY], sel[g]->p.x[i][ZZ],
- sel[g]->p.m.refid[i], sel[g]->p.m.mapid[i],
- sel[g]->p.m.mapb.index[i]+1,
- sel[g]->p.m.mapb.index[i+1]);
- }
- if (n < sel[g]->p.nr)
- {
- fprintf(stderr, " ...\n");
- }
- }
- fprintf(stderr, "\n");
- return 0;
-}
-
-static void
-print_selections(int nr, gmx_ana_selection_t **sel, int nmaxind)
-{
- int g, i, n;
-
- fprintf(stderr, "\nSelections:\n");
- for (g = 0; g < nr; ++g)
- {
- fprintf(stderr, " ");
- gmx_ana_selection_print_info(sel[g]);
- fprintf(stderr, " ");
- gmx_ana_index_dump(sel[g]->g, g, nmaxind);
-
- fprintf(stderr, " Block (size=%d):", sel[g]->p.m.mapb.nr);
- if (!sel[g]->p.m.mapb.index)
- {
- fprintf(stderr, " (null)");
- }
- else
- {
- n = sel[g]->p.m.mapb.nr;
- if (nmaxind >= 0 && n > nmaxind)
- n = nmaxind;
- for (i = 0; i <= n; ++i)
- fprintf(stderr, " %d", sel[g]->p.m.mapb.index[i]);
- if (n < sel[g]->p.m.mapb.nr)
- fprintf(stderr, " ...");
- }
- fprintf(stderr, "\n");
-
- n = sel[g]->p.m.nr;
- if (nmaxind >= 0 && n > nmaxind)
- n = nmaxind;
- fprintf(stderr, " RefId:");
- if (!sel[g]->p.m.refid)
- {
- fprintf(stderr, " (null)");
- }
- else
- {
- for (i = 0; i < n; ++i)
- fprintf(stderr, " %d", sel[g]->p.m.refid[i]);
- if (n < sel[g]->p.m.nr)
- fprintf(stderr, " ...");
- }
- fprintf(stderr, "\n");
-
- fprintf(stderr, " MapId:");
- if (!sel[g]->p.m.mapid)
- {
- fprintf(stderr, " (null)");
- }
- else
- {
- for (i = 0; i < n; ++i)
- fprintf(stderr, " %d", sel[g]->p.m.mapid[i]);
- if (n < sel[g]->p.m.nr)
- fprintf(stderr, " ...");
- }
- fprintf(stderr, "\n");
- }
- fprintf(stderr, "\n");
-}
-
-int
-gmx_test_selection(int argc, char *argv[])
-{
- const char *desc[] = {
- "This is a test program for selections.",
- };
-
- gmx_bool bMaskOnly = FALSE;
- gmx_bool bFrameTree = FALSE;
- gmx_bool bDebugCompile = FALSE;
- int nref = 0;
- int nmaxind = 20;
- t_pargs pa[] = {
- {"-mask", FALSE, etBOOL, {&bMaskOnly},
- "Test position mask functionality"},
- {"-compdebug", FALSE, etBOOL, {&bDebugCompile},
- "Print intermediate trees during compilation"},
- {"-frtree", FALSE, etBOOL, {&bFrameTree},
- "Print the whole evaluation tree for each frame"},
- {"-nref", FALSE, etINT, {&nref},
- "Number of reference selections to ask for"},
- {"-pmax", FALSE, etINT, {&nmaxind},
- "Maximum number of indices to print in lists (-1 = print all)"},
- };
-
- t_filenm fnm[] = {
- {efDAT, "-o", "debug", ffOPTWR},
- };
-
- gmx_ana_traj_t *trj;
- t_dumpdata d;
- int ngrps;
- gmx_ana_selection_t **sel;
- output_env_t oenv;
-
-#define NFILE asize(fnm)
-
- CopyRight(stderr, argv[0]);
-
- gmx_ana_traj_create(&trj, ANA_DEBUG_SELECTION | ANA_USER_SELINIT | ANA_USE_FULLGRPS);
- gmx_ana_get_selcollection(trj, &d.sc);
- gmx_ana_set_nanagrps(trj, -1);
- parse_trjana_args(trj, &argc, argv, 0,
- NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL,
- &oenv);
- if (bMaskOnly)
- {
- gmx_ana_add_flags(trj, ANA_USE_POSMASK);
- gmx_ana_selcollection_set_outpostype(d.sc, NULL, TRUE);
- }
- gmx_ana_selcollection_set_compile_debug(d.sc, bDebugCompile);
- gmx_ana_set_nrefgrps(trj, nref);
- gmx_ana_init_selections(trj);
- gmx_ana_get_ngrps(trj, &ngrps);
- gmx_ana_get_anagrps(trj, &sel);
-
- d.bFrameTree = bFrameTree;
- d.nmaxind = nmaxind;
-
- print_selections(ngrps, sel, d.nmaxind);
-
- gmx_ana_do(trj, 0, &dump_frame, &d);
-
- print_selections(ngrps, sel, d.nmaxind);
-
- gmx_ana_traj_free(trj);
- done_filenms(NFILE, fnm);
-
- return 0;
-}
-
-int
-main(int argc, char *argv[])
-{
- gmx_test_selection(argc, argv);
- return 0;
-}
+++ /dev/null
-.deps
-.libs
+++ /dev/null
-# Convenience library for statistics routine - not installed
-
-AM_CPPFLAGS= -I$(top_srcdir)/include
-
-noinst_LTLIBRARIES = libstatistics.la
-
-libstatistics_la_SOURCES = \
- gmx_statistics.c histogram.c
-
-LDADD = ../libgmx@LIBSUFFIX@.la
-
-EXTRA_PROGRAMS = gmx_statistics_test
-
-CLEANFILES = *.la *~ \\\#*
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*- */
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Green Red Orange Magenta Azure Cyan Skyblue
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <math.h>
-#include "typedefs.h"
-#include "smalloc.h"
-#include "gmx_statistics.h"
-
-static double sqr(double x)
-{
- return x*x;
-}
-
-static int gmx_nint(double x)
-{
- return (int) (x+0.5);
-}
-
-typedef struct gmx_stats {
- double aa,a,b,sigma_aa,sigma_a,sigma_b,aver,sigma_aver,error;
- double rmsd,Rdata,Rfit,Rfitaa,chi2,chi2aa;
- double *x,*y,*dx,*dy;
- int computed;
- int np,np_c,nalloc;
-} gmx_stats;
-
-gmx_stats_t gmx_stats_init()
-{
- gmx_stats *stats;
-
- snew(stats,1);
-
- return (gmx_stats_t) stats;
-}
-
-int gmx_stats_get_npoints(gmx_stats_t gstats, int *N)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
-
- *N = stats->np;
-
- return estatsOK;
-}
-
-int gmx_stats_done(gmx_stats_t gstats)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
-
- sfree(stats->x);
- stats->x = NULL;
- sfree(stats->y);
- stats->y = NULL;
- sfree(stats->dx);
- stats->dx = NULL;
- sfree(stats->dy);
- stats->dy = NULL;
-
- return estatsOK;
-}
-
-int gmx_stats_add_point(gmx_stats_t gstats,double x,double y,
- double dx,double dy)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int i;
-
- if (stats->np+1 >= stats->nalloc)
- {
- if (stats->nalloc == 0)
- stats->nalloc = 1024;
- else
- stats->nalloc *= 2;
- srenew(stats->x,stats->nalloc);
- srenew(stats->y,stats->nalloc);
- srenew(stats->dx,stats->nalloc);
- srenew(stats->dy,stats->nalloc);
- for(i=stats->np; (i<stats->nalloc); i++)
- {
- stats->x[i] = 0;
- stats->y[i] = 0;
- stats->dx[i] = 0;
- stats->dy[i] = 0;
- }
- }
- stats->x[stats->np] = x;
- stats->y[stats->np] = y;
- stats->dx[stats->np] = dx;
- stats->dy[stats->np] = dy;
- stats->np++;
- stats->computed = 0;
-
- return estatsOK;
-}
-
-int gmx_stats_get_point(gmx_stats_t gstats,real *x,real *y,
- real *dx,real *dy)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
-
- if (stats->np_c < stats->np)
- {
- if (NULL != x) *x = stats->x[stats->np_c];
- if (NULL != y) *y = stats->y[stats->np_c];
- if (NULL != dx) *dx = stats->dx[stats->np_c];
- if (NULL != dy) *dy = stats->dy[stats->np_c];
- stats->np_c++;
-
- return estatsOK;
- }
- stats->np_c = 0;
-
- return estatsNO_POINTS;
-}
-
-int gmx_stats_add_points(gmx_stats_t gstats,int n,real *x,real *y,
- real *dx,real *dy)
-{
- int i,ok;
-
- for(i=0; (i<n); i++)
- {
- if ((ok = gmx_stats_add_point(gstats,x[i],y[i],
- (NULL != dx) ? dx[i] : 0,
- (NULL != dy) ? dy[i] : 0)) != estatsOK)
- {
- return ok;
- }
- }
- return estatsOK;
-}
-
-static int gmx_stats_compute(gmx_stats *stats,int weight)
-{
- double yy,yx,xx,sx,sy,dy,chi2,chi2aa,d2;
- double ssxx,ssyy,ssxy;
- double w,wtot,yx_nw,sy_nw,sx_nw,yy_nw,xx_nw,dx2,dy2;
- int i,N;
-
- N = stats->np;
- if (stats->computed == 0)
- {
- if (N < 1)
- {
- return estatsNO_POINTS;
- }
-
- xx = xx_nw = 0;
- yy = yy_nw = 0;
- yx = yx_nw = 0;
- sx = sx_nw = 0;
- sy = sy_nw = 0;
- wtot = 0;
- d2 = 0;
- for(i=0; (i<N); i++)
- {
- d2 += sqr(stats->x[i]-stats->y[i]);
- if ((stats->dy[i]) && (weight == elsqWEIGHT_Y))
- {
- w = 1/sqr(stats->dy[i]);
- }
- else
- {
- w = 1;
- }
-
- wtot += w;
-
- xx += w*sqr(stats->x[i]);
- xx_nw += sqr(stats->x[i]);
-
- yy += w*sqr(stats->y[i]);
- yy_nw += sqr(stats->y[i]);
-
- yx += w*stats->y[i]*stats->x[i];
- yx_nw += stats->y[i]*stats->x[i];
-
- sx += w*stats->x[i];
- sx_nw += stats->x[i];
-
- sy += w*stats->y[i];
- sy_nw += stats->y[i];
- }
-
- /* Compute average, sigma and error */
- stats->aver = sy_nw/N;
- stats->sigma_aver = sqrt(yy_nw/N - sqr(sy_nw/N));
- stats->error = stats->sigma_aver/sqrt(N);
-
- /* Compute RMSD between x and y */
- stats->rmsd = sqrt(d2/N);
-
- /* Correlation coefficient for data */
- yx_nw /= N;
- xx_nw /= N;
- yy_nw /= N;
- sx_nw /= N;
- sy_nw /= N;
- ssxx = N*(xx_nw - sqr(sx_nw));
- ssyy = N*(yy_nw - sqr(sy_nw));
- ssxy = N*(yx_nw - (sx_nw*sy_nw));
- stats->Rdata = sqrt(sqr(ssxy)/(ssxx*ssyy));
-
- /* Compute straight line through datapoints, either with intercept
- zero (result in aa) or with intercept variable (results in a
- and b) */
- yx = yx/wtot;
- xx = xx/wtot;
- sx = sx/wtot;
- sy = sy/wtot;
-
- stats->aa = (yx/xx);
- stats->a = (yx-sx*sy)/(xx-sx*sx);
- stats->b = (sy)-(stats->a)*(sx);
-
- /* Compute chi2, deviation from a line y = ax+b. Also compute
- chi2aa which returns the deviation from a line y = ax. */
- chi2 = 0;
- chi2aa = 0;
- for(i=0; (i<N); i++)
- {
- if (stats->dy[i] > 0)
- {
- dy = stats->dy[i];
- }
- else
- {
- dy = 1;
- }
- chi2aa += sqr((stats->y[i]-(stats->aa*stats->x[i]))/dy);
- chi2 += sqr((stats->y[i]-(stats->a*stats->x[i]+stats->b))/dy);
- }
- if (N > 2)
- {
- stats->chi2 = sqrt(chi2/(N-2));
- stats->chi2aa = sqrt(chi2aa/(N-2));
-
- /* Look up equations! */
- dx2 = (xx-sx*sx);
- dy2 = (yy-sy*sy);
- stats->sigma_a = sqrt(stats->chi2/((N-2)*dx2));
- stats->sigma_b = stats->sigma_a*sqrt(xx);
- stats->Rfit = fabs(ssxy)/sqrt(ssxx*ssyy);
- /*stats->a*sqrt(dx2/dy2);*/
- stats->Rfitaa = stats->aa*sqrt(dx2/dy2);
- }
- else
- {
- stats->chi2 = 0;
- stats->chi2aa = 0;
- stats->sigma_a = 0;
- stats->sigma_b = 0;
- stats->Rfit = 0;
- stats->Rfitaa = 0;
- }
-
- stats->computed = 1;
- }
-
- return estatsOK;
-}
-
-int gmx_stats_get_ab(gmx_stats_t gstats,int weight,
- real *a,real *b,real *da,real *db,
- real *chi2,real *Rfit)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,weight)) != estatsOK)
- return ok;
- if (NULL != a)
- {
- *a = stats->a;
- }
- if (NULL != b)
- {
- *b = stats->b;
- }
- if (NULL != da)
- {
- *da = stats->sigma_a;
- }
- if (NULL != db)
- {
- *db = stats->sigma_b;
- }
- if (NULL != chi2)
- {
- *chi2 = stats->chi2;
- }
- if (NULL != Rfit)
- {
- *Rfit = stats->Rfit;
- }
-
- return estatsOK;
-}
-
-int gmx_stats_get_a(gmx_stats_t gstats,int weight,real *a,real *da,
- real *chi2,real *Rfit)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,weight)) != estatsOK)
- return ok;
- if (NULL != a) *a = stats->aa;
- if (NULL != da) *da = stats->sigma_aa;
- if (NULL != chi2) *chi2 = stats->chi2aa;
- if (NULL != Rfit) *Rfit = stats->Rfitaa;
-
- return estatsOK;
-}
-
-int gmx_stats_get_average(gmx_stats_t gstats,real *aver)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
- {
- return ok;
- }
-
- *aver = stats->aver;
-
- return estatsOK;
-}
-
-int gmx_stats_get_ase(gmx_stats_t gstats,real *aver,real *sigma,real *error)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
- {
- return ok;
- }
-
- if (NULL != aver)
- {
- *aver = stats->aver;
- }
- if (NULL != sigma)
- {
- *sigma = stats->sigma_aver;
- }
- if (NULL != error)
- {
- *error = stats->error;
- }
-
- return estatsOK;
-}
-
-int gmx_stats_get_sigma(gmx_stats_t gstats,real *sigma)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
- return ok;
-
- *sigma = stats->sigma_aver;
-
- return estatsOK;
-}
-
-int gmx_stats_get_error(gmx_stats_t gstats,real *error)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
- return ok;
-
- *error = stats->error;
-
- return estatsOK;
-}
-
-int gmx_stats_get_corr_coeff(gmx_stats_t gstats,real *R)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
- return ok;
-
- *R = stats->Rdata;
-
- return estatsOK;
-}
-
-int gmx_stats_get_rmsd(gmx_stats_t gstats,real *rmsd)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int ok;
-
- if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
- {
- return ok;
- }
-
- *rmsd = stats->rmsd;
-
- return estatsOK;
-}
-
-int gmx_stats_dump_xy(gmx_stats_t gstats,FILE *fp)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int i,ok;
-
- for(i=0; (i<stats->np); i++)
- {
- fprintf(fp,"%12g %12g %12g %12g\n",stats->x[i],stats->y[i],
- stats->dx[i],stats->dy[i]);
- }
-
- return estatsOK;
-}
-
-int gmx_stats_remove_outliers(gmx_stats_t gstats,double level)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int i,iter=1,done=0,ok;
- real rmsd,r;
-
- while ((stats->np >= 10) && !done)
- {
- if ((ok = gmx_stats_get_rmsd(gstats,&rmsd)) != estatsOK)
- {
- return ok;
- }
- done = 1;
- for(i=0; (i<stats->np); )
- {
- r = fabs(stats->x[i]-stats->y[i]);
- if (r > level*rmsd)
- {
- fprintf(stderr,"Removing outlier, iter = %d, rmsd = %g, x = %g, y = %g\n",
- iter,rmsd,stats->x[i],stats->y[i]);
- if (i < stats->np-1)
- {
- stats->x[i] = stats->x[stats->np-1];
- stats->y[i] = stats->y[stats->np-1];
- stats->dx[i] = stats->dx[stats->np-1];
- stats->dy[i] = stats->dy[stats->np-1];
- }
- stats->np--;
- done = 0;
- }
- else
- {
- i++;
- }
- }
- iter++;
- }
-
- return estatsOK;
-}
-
-int gmx_stats_make_histogram(gmx_stats_t gstats,real binwidth,int *nb,
- int ehisto,int normalized,real **x,real **y)
-{
- gmx_stats *stats = (gmx_stats *) gstats;
- int i,ok,index=0,nbins=*nb,*nindex;
- double minx,maxx,maxy,miny,delta,dd,minh;
-
- if (((binwidth <= 0) && (nbins <= 0)) ||
- ((binwidth > 0) && (nbins > 0)))
- {
- return estatsINVALID_INPUT;
- }
- if (stats->np <= 2)
- {
- return estatsNO_POINTS;
- }
- minx = maxx = stats->x[0];
- miny = maxy = stats->y[0];
- for(i=1; (i<stats->np); i++)
- {
- miny = (stats->y[i] < miny) ? stats->y[i] : miny;
- maxy = (stats->y[i] > maxy) ? stats->y[i] : maxy;
- minx = (stats->x[i] < minx) ? stats->x[i] : minx;
- maxx = (stats->x[i] > maxx) ? stats->x[i] : maxx;
- }
- if (ehisto == ehistoX)
- {
- delta = maxx-minx;
- minh = minx;
- }
- else if (ehisto == ehistoY)
- {
- delta = maxy-miny;
- minh = miny;
- }
- else
- return estatsINVALID_INPUT;
-
- if (binwidth == 0)
- {
- binwidth = (delta)/nbins;
- }
- else
- {
- nbins = gmx_nint((delta)/binwidth + 0.5);
- }
- snew(*x,nbins);
- snew(nindex,nbins);
- for(i=0; (i<nbins); i++)
- {
- (*x)[i] = minh + binwidth*(i+0.5);
- }
- if (normalized == 0)
- {
- dd = 1;
- }
- else
- {
- dd = 1.0/(binwidth*stats->np);
- }
-
- snew(*y,nbins);
- for(i=0; (i<stats->np); i++)
- {
- if (ehisto == ehistoY)
- index = (stats->y[i]-miny)/binwidth;
- else if (ehisto == ehistoX)
- index = (stats->x[i]-minx)/binwidth;
- if (index<0)
- {
- index = 0;
- }
- if (index>nbins-1)
- {
- index = nbins-1;
- }
- (*y)[index] += dd;
- nindex[index]++;
- }
- if (*nb == 0)
- *nb = nbins;
- for(i=0; (i<nbins); i++)
- if (nindex[i] > 0)
- (*y)[i] /= nindex[i];
-
- sfree(nindex);
-
- return estatsOK;
-}
-
-static const char *stats_error[estatsNR] =
-{
- "All well in STATS land",
- "No points",
- "Not enough memory",
- "Invalid histogram input",
- "Unknown error",
- "Not implemented yet"
-};
-
-const char *gmx_stats_message(int estats)
-{
- if ((estats >= 0) && (estats < estatsNR))
- {
- return stats_error[estats];
- }
- else
- {
- return stats_error[estatsERROR];
- }
-}
-
-/* Old convenience functions, should be merged with the core
- statistics above. */
-int lsq_y_ax(int n, real x[], real y[], real *a)
-{
- gmx_stats_t lsq = gmx_stats_init();
- int ok;
- real da,chi2,Rfit;
-
- gmx_stats_add_points(lsq,n,x,y,0,0);
- if ((ok = gmx_stats_get_a(lsq,elsqWEIGHT_NONE,a,&da,&chi2,&Rfit)) != estatsOK)
- {
- return ok;
- }
-
- /* int i;
- double xx,yx;
-
- yx=xx=0.0;
- for (i=0; i<n; i++) {
- yx+=y[i]*x[i];
- xx+=x[i]*x[i];
- }
- *a=yx/xx;
- */
- return estatsOK;
-}
-
-static int low_lsq_y_ax_b(int n, real *xr, double *xd, real yr[],
- real *a, real *b,real *r,real *chi2)
-{
- int i,ok;
- gmx_stats_t lsq;
-
- lsq = gmx_stats_init();
- for(i=0; (i<n); i++)
- {
- if ((ok = gmx_stats_add_point(lsq,(NULL != xd) ? xd[i] : xr[i],yr[i],0,0))
- != estatsOK)
- {
- return ok;
- }
- }
- if ((ok = gmx_stats_get_ab(lsq,elsqWEIGHT_NONE,a,b,NULL,NULL,chi2,r)) != estatsOK)
- {
- return ok;
- }
-
- return estatsOK;
- /*
- double x,y,yx,xx,yy,sx,sy,chi2;
-
- yx=xx=yy=sx=sy=0.0;
- for (i=0; i<n; i++) {
- if (xd != NULL) {
- x = xd[i];
- } else {
- x = xr[i];
- }
- y = yr[i];
-
- yx += y*x;
- xx += x*x;
- yy += y*y;
- sx += x;
- sy += y;
- }
- *a = (n*yx-sy*sx)/(n*xx-sx*sx);
- *b = (sy-(*a)*sx)/n;
- *r = sqrt((xx-sx*sx)/(yy-sy*sy));
-
- chi2 = 0;
- if (xd != NULL) {
- for(i=0; i<n; i++)
- chi2 += sqr(yr[i] - ((*a)*xd[i] + (*b)));
- } else {
- for(i=0; i<n; i++)
- chi2 += sqr(yr[i] - ((*a)*xr[i] + (*b)));
- }
-
- if (n > 2)
- return sqrt(chi2/(n-2));
- else
- return 0;
- */
-}
-
-int lsq_y_ax_b(int n, real x[], real y[], real *a, real *b,real *r,real *chi2)
-{
- return low_lsq_y_ax_b(n,x,NULL,y,a,b,r,chi2);
-}
-
-int lsq_y_ax_b_xdouble(int n, double x[], real y[], real *a, real *b,
- real *r,real *chi2)
-{
- return low_lsq_y_ax_b(n,NULL,x,y,a,b,r,chi2);
-}
-
-int lsq_y_ax_b_error(int n, real x[], real y[], real dy[],
- real *a, real *b, real *da, real *db,
- real *r,real *chi2)
-{
- gmx_stats_t lsq;
- int i,ok;
-
- lsq = gmx_stats_init();
- for(i=0; (i<n); i++)
- {
- if ((ok = gmx_stats_add_point(lsq,x[i],y[i],0,dy[i])) != estatsOK)
- {
- return ok;
- }
- }
- if ((ok = gmx_stats_get_ab(lsq,elsqWEIGHT_Y,a,b,da,db,chi2,r)) != estatsOK)
- {
- return ok;
- }
- if ((ok = gmx_stats_done(lsq)) != estatsOK)
- {
- return ok;
- }
- sfree(lsq);
-
- return estatsOK;
- /*
- double sxy,sxx,syy,sx,sy,w,s_2,dx2,dy2,mins;
-
- sxy=sxx=syy=sx=sy=w=0.0;
- mins = dy[0];
- for(i=1; (i<n); i++)
- mins = min(mins,dy[i]);
- if (mins <= 0)
- gmx_fatal(FARGS,"Zero or negative weigths in linear regression analysis");
-
- for (i=0; i<n; i++) {
- s_2 = sqr(1.0/dy[i]);
- sxx += s_2*sqr(x[i]);
- sxy += s_2*y[i]*x[i];
- syy += s_2*sqr(y[i]);
- sx += s_2*x[i];
- sy += s_2*y[i];
- w += s_2;
- }
- sxx = sxx/w;
- sxy = sxy/w;
- syy = syy/w;
- sx = sx/w;
- sy = sy/w;
- dx2 = (sxx-sx*sx);
- dy2 = (syy-sy*sy);
- *a=(sxy-sy*sx)/dx2;
- *b=(sy-(*a)*sx);
-
- *chi2=0;
- for(i=0; i<n; i++)
- *chi2+=sqr((y[i]-((*a)*x[i]+(*b)))/dy[i]);
- *chi2 = *chi2/w;
-
- *da = sqrt(*chi2/((n-2)*dx2));
- *db = *da*sqrt(sxx);
- *r = *a*sqrt(dx2/dy2);
-
- if (debug)
- fprintf(debug,"sx = %g, sy = %g, sxy = %g, sxx = %g, w = %g\n"
- "chi2 = %g, dx2 = %g\n",
- sx,sy,sxy,sxx,w,*chi2,dx2);
-
- if (n > 2)
- *chi2 = sqrt(*chi2/(n-2));
- else
- *chi2 = 0;
- */
-}
-
-
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
- */
-/* This file is completely threadsafe - keep it that way! */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef GMX_CRAY_XT3
-#undef HAVE_PWD_H
-#endif
-
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <time.h>
-
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-#include <time.h>
-
-#include "typedefs.h"
-#include "smalloc.h"
-#include "gmx_fatal.h"
-#include "macros.h"
-#include "string2.h"
-#include "futil.h"
-
-int continuing(char *s)
-/* strip trailing spaces and if s ends with a CONTINUE remove that too.
- * returns TRUE if s ends with a CONTINUE, FALSE otherwise.
- */
-{
- int sl;
-
- rtrim(s);
- sl = strlen(s);
- if ((sl > 0) && (s[sl-1] == CONTINUE)) {
- s[sl-1] = 0;
- return TRUE;
- }
- else
- return FALSE;
-}
-
-
-
-char *fgets2(char *line, int n, FILE *stream)
-/* This routine reads a string from stream of max length n
- * and zero terminated, without newlines
- * line should be long enough (>= n)
- */
-{
- char *c;
- if (fgets(line,n,stream) == NULL) {
- return NULL;
- }
- if ((c=strchr(line,'\n')) != NULL) {
- *c = '\0';
- } else {
- /* A line not ending in a newline can only occur at the end of a file,
- * or because of n being too small.
- * Since both cases occur very infrequently, we can check for EOF.
- */
- if (!gmx_eof(stream)) {
- gmx_fatal(FARGS,"An input file contains a line longer than %d characters, while the buffer passed to fgets2 has size %d. The line starts with: '%20.20s'",n,n,line);
- }
- }
- if ((c=strchr(line,'\r')) != NULL) {
- *c = '\0';
- }
-
- return line;
-}
-
-void strip_comment (char *line)
-{
- char *c;
-
- if (!line)
- return;
-
- /* search for a comment mark and replace it by a zero */
- if ((c = strchr(line,COMMENTSIGN)) != NULL)
- (*c) = 0;
-}
-
-void upstring (char *str)
-{
- int i;
-
- for (i=0; (i < (int)strlen(str)); i++)
- str[i] = toupper(str[i]);
-}
-
-void ltrim (char *str)
-{
- char *tr;
- int i,c;
-
- if (NULL == str)
- return;
-
- c = 0;
- while (('\0' != str[c]) && isspace(str[c]))
- c++;
- if (c > 0)
- {
- for(i=c; ('\0' != str[i]); i++)
- str[i-c] = str[i];
- str[i-c] = '\0';
- }
-}
-
-void rtrim (char *str)
-{
- int nul;
-
- if (NULL == str)
- return;
-
- nul = strlen(str)-1;
- while ((nul > 0) && ((str[nul] == ' ') || (str[nul] == '\t')) ) {
- str[nul] = '\0';
- nul--;
- }
-}
-
-void trim (char *str)
-{
- ltrim (str);
- rtrim (str);
-}
-
-char *
-gmx_ctime_r(const time_t *clock,char *buf, int n)
-{
- char tmpbuf[STRLEN];
-
-#if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
- /* Windows */
- ctime_s( tmpbuf, STRLEN, clock );
-#else
- ctime_r(clock,tmpbuf);
-#endif
- strncpy(buf,tmpbuf,n-1);
- buf[n-1]='\0';
-
- return buf;
-}
-
-void nice_header (FILE *out,const char *fn)
-{
- const char *unk = "onbekend";
- time_t clock;
- char *user=NULL;
- int gh;
- uid_t uid;
- char buf[256];
- char timebuf[STRLEN];
-#ifdef HAVE_PWD_H
- struct passwd *pw;
-#endif
-
- /* Print a nice header above the file */
- time(&clock);
- fprintf (out,"%c\n",COMMENTSIGN);
- fprintf (out,"%c\tFile '%s' was generated\n",COMMENTSIGN,fn ? fn : unk);
-
-#ifdef HAVE_PWD_H
- uid = getuid();
- pw = getpwuid(uid);
- gh = gethostname(buf,255);
- user= pw->pw_name;
-#else
- uid = 0;
- gh = -1;
-#endif
-
- gmx_ctime_r(&clock,timebuf,STRLEN);
- fprintf (out,"%c\tBy user: %s (%d)\n",COMMENTSIGN,
- user ? user : unk,(int) uid);
- fprintf(out,"%c\tOn host: %s\n",COMMENTSIGN,(gh == 0) ? buf : unk);
-
- fprintf (out,"%c\tAt date: %s",COMMENTSIGN,timebuf);
- fprintf (out,"%c\n",COMMENTSIGN);
-}
-
-int gmx_strcasecmp_min(const char *str1, const char *str2)
-{
- char ch1,ch2;
-
- do
- {
- do
- ch1=toupper(*(str1++));
- while ((ch1=='-') || (ch1=='_'));
- do
- ch2=toupper(*(str2++));
- while ((ch2=='-') || (ch2=='_'));
- if (ch1!=ch2) return (ch1-ch2);
- }
- while (ch1);
- return 0;
-}
-
-int gmx_strncasecmp_min(const char *str1, const char *str2, int n)
-{
- char ch1,ch2;
- char *stri1, *stri2;
-
- stri1=(char *)str1;
- stri2=(char *)str2;
- do
- {
- do
- ch1=toupper(*(str1++));
- while ((ch1=='-') || (ch1=='_'));
- do
- ch2=toupper(*(str2++));
- while ((ch2=='-') || (ch2=='_'));
- if (ch1!=ch2) return (ch1-ch2);
- }
- while (ch1 && (str1-stri1<n) && (str2-stri2<n));
- return 0;
-}
-
-int gmx_strcasecmp(const char *str1, const char *str2)
-{
- char ch1,ch2;
-
- do
- {
- ch1=toupper(*(str1++));
- ch2=toupper(*(str2++));
- if (ch1!=ch2) return (ch1-ch2);
- }
- while (ch1);
- return 0;
-}
-
-int gmx_strncasecmp(const char *str1, const char *str2, int n)
-{
- char ch1,ch2;
-
- if(n==0)
- return 0;
-
- do
- {
- ch1=toupper(*(str1++));
- ch2=toupper(*(str2++));
- if (ch1!=ch2) return (ch1-ch2);
- n--;
- }
- while (ch1 && n);
- return 0;
-}
-
-char *gmx_strdup(const char *src)
-{
- char *dest;
-
- snew(dest,strlen(src)+1);
- strcpy(dest,src);
-
- return dest;
-}
-
-char *
-gmx_strndup(const char *src, int n)
-{
- int len;
- char *dest;
-
- len = strlen(src);
- if (len > n)
- {
- len = n;
- }
- snew(dest, len+1);
- strncpy(dest, src, len);
- dest[len] = 0;
- return dest;
-}
-
-/*!
- * \param[in] pattern Pattern to match against.
- * \param[in] str String to match.
- * \returns 0 on match, GMX_NO_WCMATCH if there is no match.
- *
- * Matches \p str against \p pattern, which may contain * and ? wildcards.
- * All other characters are matched literally.
- * Currently, it is not possible to match literal * or ?.
- */
-int
-gmx_wcmatch(const char *pattern, const char *str)
-{
- while (*pattern)
- {
- if (*pattern == '*')
- {
- /* Skip multiple wildcards in a sequence */
- while (*pattern == '*' || *pattern == '?')
- {
- ++pattern;
- /* For ?, we need to check that there are characters left
- * in str. */
- if (*pattern == '?')
- {
- if (*str == 0)
- {
- return GMX_NO_WCMATCH;
- }
- else
- {
- ++str;
- }
- }
- }
- /* If the pattern ends after the star, we have a match */
- if (*pattern == 0)
- {
- return 0;
- }
- /* Match the rest against each possible suffix of str */
- while (*str)
- {
- /* Only do the recursive call if the first character
- * matches. We don't have to worry about wildcards here,
- * since we have processed them above. */
- if (*pattern == *str)
- {
- int rc;
- /* Match the suffix, and return if a match or an error */
- rc = gmx_wcmatch(pattern, str);
- if (rc != GMX_NO_WCMATCH)
- {
- return rc;
- }
- }
- ++str;
- }
- /* If no suffix of str matches, we don't have a match */
- return GMX_NO_WCMATCH;
- }
- else if ((*pattern == '?' && *str != 0) || *pattern == *str)
- {
- ++str;
- }
- else
- {
- return GMX_NO_WCMATCH;
- }
- ++pattern;
- }
- /* When the pattern runs out, we have a match if the string has ended. */
- return (*str == 0) ? 0 : GMX_NO_WCMATCH;
-}
-
-char *wrap_lines(const char *buf,int line_width, int indent,gmx_bool bIndentFirst)
-{
- char *b2;
- int i,i0,i2,j,b2len,lspace=0,l2space=0;
- gmx_bool bFirst,bFitsOnLine;
-
- /* characters are copied from buf to b2 with possible spaces changed
- * into newlines and extra space added for indentation.
- * i indexes buf (source buffer) and i2 indexes b2 (destination buffer)
- * i0 points to the beginning of the current line (in buf, source)
- * lspace and l2space point to the last space on the current line
- * bFirst is set to prevent indentation of first line
- * bFitsOnLine says if the first space occurred before line_width, if
- * that is not the case, we have a word longer than line_width which
- * will also not fit on the next line, so we might as well keep it on
- * the current line (where it also won't fit, but looks better)
- */
-
- b2=NULL;
- b2len=strlen(buf)+1+indent;
- snew(b2,b2len);
- i0=i2=0;
- if (bIndentFirst)
- for(i2=0; (i2<indent); i2++)
- b2[i2] = ' ';
- bFirst=TRUE;
- do {
- l2space = -1;
- /* find the last space before end of line */
- for(i=i0; ((i-i0 < line_width) || (l2space==-1)) && (buf[i]); i++) {
- b2[i2++] = buf[i];
- /* remember the position of a space */
- if (buf[i] == ' ') {
- lspace = i;
- l2space = i2-1;
- }
- /* if we have a newline before the line is full, reset counters */
- if (buf[i]=='\n' && buf[i+1]) {
- i0=i+1;
- b2len+=indent;
- srenew(b2, b2len);
- /* add indentation after the newline */
- for(j=0; (j<indent); j++)
- b2[i2++]=' ';
- }
- }
- /* If we are at the last newline, copy it */
- if (buf[i]=='\n' && !buf[i+1]) {
- b2[i2++] = buf[i++];
- }
- /* if we're not at the end of the string */
- if (buf[i]) {
- /* check if one word does not fit on the line */
- bFitsOnLine = (i-i0 <= line_width);
- /* reset line counters to just after the space */
- i0 = lspace+1;
- i2 = l2space+1;
- /* if the words fit on the line, and we're beyond the indentation part */
- if ( (bFitsOnLine) && (l2space >= indent) ) {
- /* start a new line */
- b2[l2space] = '\n';
- /* and add indentation */
- if (indent) {
- if (bFirst) {
- line_width-=indent;
- bFirst=FALSE;
- }
- b2len+=indent;
- srenew(b2, b2len);
- for(j=0; (j<indent); j++)
- b2[i2++]=' ';
- /* no extra spaces after indent; */
- while(buf[i0]==' ')
- i0++;
- }
- }
- }
- } while (buf[i]);
- b2[i2] = '\0';
-
- return b2;
-}
-
-char **split(char sep,char *str)
-{
- char **ptr = NULL;
- int n,nn,nptr = 0;
-
- if (str == NULL)
- return NULL;
- nn = strlen(str);
- for(n=0; (n<nn); n++)
- if (str[n] == sep)
- nptr++;
- snew(ptr,nptr+2);
- nptr = 0;
- while (*str != '\0') {
- while ((*str != '\0') && (*str == sep))
- str++;
- if (*str != '\0') {
- snew(ptr[nptr],1+strlen(str));
- n = 0;
- while ((*str != '\0') && (*str != sep)) {
- ptr[nptr][n] = *str;
- str++;
- n++;
- }
- ptr[nptr][n] = '\0';
- nptr++;
- }
- }
- ptr[nptr] = NULL;
-
- return ptr;
-}
-
-
-gmx_large_int_t
-str_to_large_int_t(const char *str, char **endptr)
-{
- int sign = 1;
- gmx_large_int_t val = 0;
- char ch;
- const char *p;
-
- p = str;
- if(p==NULL)
- {
- *endptr=NULL;
- return 0;
- }
-
- /* Strip off initial white space */
- while(isspace(*p))
- {
- p++;
- }
- /* Conform to ISO C99 - return original pointer if string does not contain a number */
- if(*str=='\0')
- {
- *endptr=(char *)str;
- }
-
- if(*p=='-')
- {
- p++;
- sign *= -1;
- }
-
- while( ((ch=*p) != '\0') && isdigit(ch) )
- {
- /* Important to add sign here, so we dont overflow in final multiplication */
- ch = (ch-'0')*sign;
- val = val*10 + ch;
- if(ch != val%10)
- {
- /* Some sort of overflow has occured, set endptr to original string */
- *endptr=(char *)str;
- errno = ERANGE;
- return(0);
- }
- p++;
- }
-
- *endptr=(char *)p;
-
- return val;
-}
-
-char *gmx_strsep(char **stringp, const char *delim)
-{
- char *ret;
- int len=strlen(delim);
- int i,j=0;
- int found=0;
-
- if (! *stringp)
- return NULL;
- ret=*stringp;
- do
- {
- if ( (*stringp)[j] == '\0')
- {
- found=1;
- *stringp=NULL;
- break;
- }
- for (i=0;i<len;i++)
- {
- if ( (*stringp)[j]==delim[i])
- {
- (*stringp)[j]='\0';
- *stringp=*stringp+j+1;
- found=1;
- break;
- }
- }
- j++;
- } while (!found);
-
- return ret;
-}
-
+++ /dev/null
-# Cross-platform threading library/MPI implementation.
-#
-# This Makefile.am can be used both for linking the final executables against
-# the libthread_mpi.a object created here, or for linking into libraries.
-#
-
-
-AM_CPPFLAGS= -I$(top_srcdir)/include
-
-noinst_LTLIBRARIES = libthread_mpi.la
-
-# again, we assume that we're using pthreads if we're using autotools.
-libthread_mpi_la_SOURCES = alltoall.c impl.h pthreads.h \
- barrier.c list.c reduce.c \
- bcast.c lock.c reduce_fast.c \
- collective.c once.c scatter.c \
- collective.h p2p.h settings.h \
- comm.c p2p_protocol.c tmpi_init.c \
- errhandler.c p2p_send_recv.c tmpi_ops.h \
- event.c p2p_wait.c topology.c \
- gather.c profile.c type.c \
- group.c profile.h winthreads.c \
- hwinfo.c pthreads.c winthreads.h
-
-
-CLEANFILES = *.la *~ \\\#*
-
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-/* This file is completely threadsafe - keep it that way! */
-#ifdef GMX_THREADS
-#include <thread_mpi.h>
-#endif
-
-
-#include <ctype.h>
-#include "sysstuff.h"
-#include "smalloc.h"
-#include "string2.h"
-#include "gmx_fatal.h"
-#include "macros.h"
-#include "names.h"
-#include "symtab.h"
-#include "futil.h"
-#include "filenm.h"
-#include "gmxfio.h"
-#include "topsort.h"
-#include "tpxio.h"
-#include "txtdump.h"
-#include "confio.h"
-#include "atomprop.h"
-#include "copyrite.h"
-#include "vec.h"
-#include "mtop_util.h"
-
-/* This number should be increased whenever the file format changes! */
-static const int tpx_version = 73;
-
-/* This number should only be increased when you edit the TOPOLOGY section
- * of the tpx format. This way we can maintain forward compatibility too
- * for all analysis tools and/or external programs that only need to
- * know the atom/residue names, charges, and bond connectivity.
- *
- * It first appeared in tpx version 26, when I also moved the inputrecord
- * to the end of the tpx file, so we can just skip it if we only
- * want the topology.
- */
-static const int tpx_generation = 23;
-
-/* This number should be the most recent backwards incompatible version
- * I.e., if this number is 9, we cannot read tpx version 9 with this code.
- */
-static const int tpx_incompatible_version = 9;
-
-
-
-/* Struct used to maintain tpx compatibility when function types are added */
-typedef struct {
- int fvnr; /* file version number in which the function type first appeared */
- int ftype; /* function type */
-} t_ftupd;
-
-/*
- *The entries should be ordered in:
- * 1. ascending file version number
- * 2. ascending function type number
- */
-/*static const t_ftupd ftupd[] = {
- { 20, F_CUBICBONDS },
- { 20, F_CONNBONDS },
- { 20, F_HARMONIC },
- { 20, F_EQM, },
- { 22, F_DISRESVIOL },
- { 22, F_ORIRES },
- { 22, F_ORIRESDEV },
- { 26, F_FOURDIHS },
- { 26, F_PIDIHS },
- { 26, F_DIHRES },
- { 26, F_DIHRESVIOL },
- { 30, F_CROSS_BOND_BONDS },
- { 30, F_CROSS_BOND_ANGLES },
- { 30, F_UREY_BRADLEY },
- { 30, F_POLARIZATION }
- };*/
-
-/*
- *The entries should be ordered in:
- * 1. ascending function type number
- * 2. ascending file version number
- */
-static const t_ftupd ftupd[] = {
- { 20, F_CUBICBONDS },
- { 20, F_CONNBONDS },
- { 20, F_HARMONIC },
- { 34, F_FENEBONDS },
- { 43, F_TABBONDS },
- { 43, F_TABBONDSNC },
- { 70, F_RESTRBONDS },
- { 30, F_CROSS_BOND_BONDS },
- { 30, F_CROSS_BOND_ANGLES },
- { 30, F_UREY_BRADLEY },
- { 34, F_QUARTIC_ANGLES },
- { 43, F_TABANGLES },
- { 26, F_FOURDIHS },
- { 26, F_PIDIHS },
- { 43, F_TABDIHS },
- { 65, F_CMAP },
- { 60, F_GB12 },
- { 61, F_GB13 },
- { 61, F_GB14 },
- { 72, F_GBPOL },
- { 72, F_NPSOLVATION },
- { 41, F_LJC14_Q },
- { 41, F_LJC_PAIRS_NB },
- { 32, F_BHAM_LR },
- { 32, F_RF_EXCL },
- { 32, F_COUL_RECIP },
- { 46, F_DPD },
- { 30, F_POLARIZATION },
- { 36, F_THOLE_POL },
- { 22, F_DISRESVIOL },
- { 22, F_ORIRES },
- { 22, F_ORIRESDEV },
- { 26, F_DIHRES },
- { 26, F_DIHRESVIOL },
- { 49, F_VSITE4FDN },
- { 50, F_VSITEN },
- { 46, F_COM_PULL },
- { 20, F_EQM },
- { 46, F_ECONSERVED },
- { 69, F_VTEMP },
- { 66, F_PDISPCORR },
- { 54, F_DHDL_CON },
-};
-#define NFTUPD asize(ftupd)
-
-/* Needed for backward compatibility */
-#define MAXNODES 256
-
-static void _do_section(t_fileio *fio,int key,gmx_bool bRead,const char *src,
- int line)
-{
- char buf[STRLEN];
- gmx_bool bDbg;
-
- if (gmx_fio_getftp(fio) == efTPA) {
- if (!bRead) {
- gmx_fio_write_string(fio,itemstr[key]);
- bDbg = gmx_fio_getdebug(fio);
- gmx_fio_setdebug(fio,FALSE);
- gmx_fio_write_string(fio,comment_str[key]);
- gmx_fio_setdebug(fio,bDbg);
- }
- else {
- if (gmx_fio_getdebug(fio))
- fprintf(stderr,"Looking for section %s (%s, %d)",
- itemstr[key],src,line);
-
- do {
- gmx_fio_do_string(fio,buf);
- } while ((gmx_strcasecmp(buf,itemstr[key]) != 0));
-
- if (gmx_strcasecmp(buf,itemstr[key]) != 0)
- gmx_fatal(FARGS,"\nCould not find section heading %s",itemstr[key]);
- else if (gmx_fio_getdebug(fio))
- fprintf(stderr," and found it\n");
- }
- }
-}
-
-#define do_section(fio,key,bRead) _do_section(fio,key,bRead,__FILE__,__LINE__)
-
-/**************************************************************
- *
- * Now the higer level routines that do io of the structures and arrays
- *
- **************************************************************/
-static void do_pullgrp(t_fileio *fio, t_pullgrp *pgrp, gmx_bool bRead,
- int file_version)
-{
- gmx_bool bDum=TRUE;
- int i;
-
- gmx_fio_do_int(fio,pgrp->nat);
- if (bRead)
- snew(pgrp->ind,pgrp->nat);
- bDum=gmx_fio_ndo_int(fio,pgrp->ind,pgrp->nat);
- gmx_fio_do_int(fio,pgrp->nweight);
- if (bRead)
- snew(pgrp->weight,pgrp->nweight);
- bDum=gmx_fio_ndo_real(fio,pgrp->weight,pgrp->nweight);
- gmx_fio_do_int(fio,pgrp->pbcatom);
- gmx_fio_do_rvec(fio,pgrp->vec);
- gmx_fio_do_rvec(fio,pgrp->init);
- gmx_fio_do_real(fio,pgrp->rate);
- gmx_fio_do_real(fio,pgrp->k);
- if (file_version >= 56) {
- gmx_fio_do_real(fio,pgrp->kB);
- } else {
- pgrp->kB = pgrp->k;
- }
-}
-
-static void do_pull(t_fileio *fio, t_pull *pull,gmx_bool bRead, int file_version)
-{
- int g;
-
- gmx_fio_do_int(fio,pull->ngrp);
- gmx_fio_do_int(fio,pull->eGeom);
- gmx_fio_do_ivec(fio,pull->dim);
- gmx_fio_do_real(fio,pull->cyl_r1);
- gmx_fio_do_real(fio,pull->cyl_r0);
- gmx_fio_do_real(fio,pull->constr_tol);
- gmx_fio_do_int(fio,pull->nstxout);
- gmx_fio_do_int(fio,pull->nstfout);
- if (bRead)
- snew(pull->grp,pull->ngrp+1);
- for(g=0; g<pull->ngrp+1; g++)
- do_pullgrp(fio,&pull->grp[g],bRead,file_version);
-}
-
-static void do_inputrec(t_fileio *fio, t_inputrec *ir,gmx_bool bRead,
- int file_version, real *fudgeQQ)
-{
- int i,j,k,*tmp,idum=0;
- gmx_bool bDum=TRUE;
- real rdum,bd_temp;
- rvec vdum;
- gmx_bool bSimAnn;
- real zerotemptime,finish_t,init_temp,finish_temp;
-
- if (file_version != tpx_version)
- {
- /* Give a warning about features that are not accessible */
- fprintf(stderr,"Note: tpx file_version %d, software version %d\n",
- file_version,tpx_version);
- }
-
- if (bRead)
- {
- init_inputrec(ir);
- }
-
- if (file_version == 0)
- {
- return;
- }
-
- /* Basic inputrec stuff */
- gmx_fio_do_int(fio,ir->eI);
- if (file_version >= 62) {
- gmx_fio_do_gmx_large_int(fio, ir->nsteps);
- } else {
- gmx_fio_do_int(fio,idum);
- ir->nsteps = idum;
- }
- if(file_version > 25) {
- if (file_version >= 62) {
- gmx_fio_do_gmx_large_int(fio, ir->init_step);
- } else {
- gmx_fio_do_int(fio,idum);
- ir->init_step = idum;
- }
- } else {
- ir->init_step=0;
- }
-
- if(file_version >= 58)
- gmx_fio_do_int(fio,ir->simulation_part);
- else
- ir->simulation_part=1;
-
- if (file_version >= 67) {
- gmx_fio_do_int(fio,ir->nstcalcenergy);
- } else {
- ir->nstcalcenergy = 1;
- }
- if (file_version < 53) {
- /* The pbc info has been moved out of do_inputrec,
- * since we always want it, also without reading the inputrec.
- */
- gmx_fio_do_int(fio,ir->ePBC);
- if ((file_version <= 15) && (ir->ePBC == 2))
- ir->ePBC = epbcNONE;
- if (file_version >= 45) {
- gmx_fio_do_int(fio,ir->bPeriodicMols);
- } else {
- if (ir->ePBC == 2) {
- ir->ePBC = epbcXYZ;
- ir->bPeriodicMols = TRUE;
- } else {
- ir->bPeriodicMols = FALSE;
- }
- }
- }
- gmx_fio_do_int(fio,ir->ns_type);
- gmx_fio_do_int(fio,ir->nstlist);
- gmx_fio_do_int(fio,ir->ndelta);
- if (file_version < 41) {
- gmx_fio_do_int(fio,idum);
- gmx_fio_do_int(fio,idum);
- }
- if (file_version >= 45)
- gmx_fio_do_real(fio,ir->rtpi);
- else
- ir->rtpi = 0.05;
- gmx_fio_do_int(fio,ir->nstcomm);
- if (file_version > 34)
- gmx_fio_do_int(fio,ir->comm_mode);
- else if (ir->nstcomm < 0)
- ir->comm_mode = ecmANGULAR;
- else
- ir->comm_mode = ecmLINEAR;
- ir->nstcomm = abs(ir->nstcomm);
-
- if(file_version > 25)
- gmx_fio_do_int(fio,ir->nstcheckpoint);
- else
- ir->nstcheckpoint=0;
-
- gmx_fio_do_int(fio,ir->nstcgsteep);
-
- if(file_version>=30)
- gmx_fio_do_int(fio,ir->nbfgscorr);
- else if (bRead)
- ir->nbfgscorr = 10;
-
- gmx_fio_do_int(fio,ir->nstlog);
- gmx_fio_do_int(fio,ir->nstxout);
- gmx_fio_do_int(fio,ir->nstvout);
- gmx_fio_do_int(fio,ir->nstfout);
- gmx_fio_do_int(fio,ir->nstenergy);
- gmx_fio_do_int(fio,ir->nstxtcout);
- if (file_version >= 59) {
- gmx_fio_do_double(fio,ir->init_t);
- gmx_fio_do_double(fio,ir->delta_t);
- } else {
- gmx_fio_do_real(fio,rdum);
- ir->init_t = rdum;
- gmx_fio_do_real(fio,rdum);
- ir->delta_t = rdum;
- }
- gmx_fio_do_real(fio,ir->xtcprec);
- if (file_version < 19) {
- gmx_fio_do_int(fio,idum);
- gmx_fio_do_int(fio,idum);
- }
- if(file_version < 18)
- gmx_fio_do_int(fio,idum);
- gmx_fio_do_real(fio,ir->rlist);
- if (file_version >= 67) {
- gmx_fio_do_real(fio,ir->rlistlong);
- }
- gmx_fio_do_int(fio,ir->coulombtype);
- if (file_version < 32 && ir->coulombtype == eelRF)
- ir->coulombtype = eelRF_NEC;
- gmx_fio_do_real(fio,ir->rcoulomb_switch);
- gmx_fio_do_real(fio,ir->rcoulomb);
- gmx_fio_do_int(fio,ir->vdwtype);
- gmx_fio_do_real(fio,ir->rvdw_switch);
- gmx_fio_do_real(fio,ir->rvdw);
- if (file_version < 67) {
- ir->rlistlong = max_cutoff(ir->rlist,max_cutoff(ir->rvdw,ir->rcoulomb));
- }
- gmx_fio_do_int(fio,ir->eDispCorr);
- gmx_fio_do_real(fio,ir->epsilon_r);
- if (file_version >= 37) {
- gmx_fio_do_real(fio,ir->epsilon_rf);
- } else {
- if (EEL_RF(ir->coulombtype)) {
- ir->epsilon_rf = ir->epsilon_r;
- ir->epsilon_r = 1.0;
- } else {
- ir->epsilon_rf = 1.0;
- }
- }
- if (file_version >= 29)
- gmx_fio_do_real(fio,ir->tabext);
- else
- ir->tabext=1.0;
-
- if(file_version > 25) {
- gmx_fio_do_int(fio,ir->gb_algorithm);
- gmx_fio_do_int(fio,ir->nstgbradii);
- gmx_fio_do_real(fio,ir->rgbradii);
- gmx_fio_do_real(fio,ir->gb_saltconc);
- gmx_fio_do_int(fio,ir->implicit_solvent);
- } else {
- ir->gb_algorithm=egbSTILL;
- ir->nstgbradii=1;
- ir->rgbradii=1.0;
- ir->gb_saltconc=0;
- ir->implicit_solvent=eisNO;
- }
- if(file_version>=55)
- {
- gmx_fio_do_real(fio,ir->gb_epsilon_solvent);
- gmx_fio_do_real(fio,ir->gb_obc_alpha);
- gmx_fio_do_real(fio,ir->gb_obc_beta);
- gmx_fio_do_real(fio,ir->gb_obc_gamma);
- if(file_version>=60)
- {
- gmx_fio_do_real(fio,ir->gb_dielectric_offset);
- gmx_fio_do_int(fio,ir->sa_algorithm);
- }
- else
- {
- ir->gb_dielectric_offset = 0.009;
- ir->sa_algorithm = esaAPPROX;
- }
- gmx_fio_do_real(fio,ir->sa_surface_tension);
-
- /* Override sa_surface_tension if it is not changed in the mpd-file */
- if(ir->sa_surface_tension<0)
- {
- if(ir->gb_algorithm==egbSTILL)
- {
- ir->sa_surface_tension = 0.0049 * 100 * CAL2JOULE;
- }
- else if(ir->gb_algorithm==egbHCT || ir->gb_algorithm==egbOBC)
- {
- ir->sa_surface_tension = 0.0054 * 100 * CAL2JOULE;
- }
- }
-
- }
- else
- {
- /* Better use sensible values than insane (0.0) ones... */
- ir->gb_epsilon_solvent = 80;
- ir->gb_obc_alpha = 1.0;
- ir->gb_obc_beta = 0.8;
- ir->gb_obc_gamma = 4.85;
- ir->sa_surface_tension = 2.092;
- }
-
-
- gmx_fio_do_int(fio,ir->nkx);
- gmx_fio_do_int(fio,ir->nky);
- gmx_fio_do_int(fio,ir->nkz);
- gmx_fio_do_int(fio,ir->pme_order);
- gmx_fio_do_real(fio,ir->ewald_rtol);
-
- if (file_version >=24)
- gmx_fio_do_int(fio,ir->ewald_geometry);
- else
- ir->ewald_geometry=eewg3D;
-
- if (file_version <=17) {
- ir->epsilon_surface=0;
- if (file_version==17)
- gmx_fio_do_int(fio,idum);
- }
- else
- gmx_fio_do_real(fio,ir->epsilon_surface);
-
- gmx_fio_do_gmx_bool(fio,ir->bOptFFT);
-
- gmx_fio_do_gmx_bool(fio,ir->bContinuation);
- gmx_fio_do_int(fio,ir->etc);
- /* before version 18, ir->etc was a gmx_bool (ir->btc),
- * but the values 0 and 1 still mean no and
- * berendsen temperature coupling, respectively.
- */
- if (file_version >= 71)
- {
- gmx_fio_do_int(fio,ir->nsttcouple);
- }
- else
- {
- ir->nsttcouple = ir->nstcalcenergy;
- }
- if (file_version <= 15)
- {
- gmx_fio_do_int(fio,idum);
- }
- if (file_version <=17)
- {
- gmx_fio_do_int(fio,ir->epct);
- if (file_version <= 15)
- {
- if (ir->epct == 5)
- {
- ir->epct = epctSURFACETENSION;
- }
- gmx_fio_do_int(fio,idum);
- }
- ir->epct -= 1;
- /* we have removed the NO alternative at the beginning */
- if(ir->epct==-1)
- {
- ir->epc=epcNO;
- ir->epct=epctISOTROPIC;
- }
- else
- {
- ir->epc=epcBERENDSEN;
- }
- }
- else
- {
- gmx_fio_do_int(fio,ir->epc);
- gmx_fio_do_int(fio,ir->epct);
- }
- if (file_version >= 71)
- {
- gmx_fio_do_int(fio,ir->nstpcouple);
- }
- else
- {
- ir->nstpcouple = ir->nstcalcenergy;
- }
- gmx_fio_do_real(fio,ir->tau_p);
- if (file_version <= 15) {
- gmx_fio_do_rvec(fio,vdum);
- clear_mat(ir->ref_p);
- for(i=0; i<DIM; i++)
- ir->ref_p[i][i] = vdum[i];
- } else {
- gmx_fio_do_rvec(fio,ir->ref_p[XX]);
- gmx_fio_do_rvec(fio,ir->ref_p[YY]);
- gmx_fio_do_rvec(fio,ir->ref_p[ZZ]);
- }
- if (file_version <= 15) {
- gmx_fio_do_rvec(fio,vdum);
- clear_mat(ir->compress);
- for(i=0; i<DIM; i++)
- ir->compress[i][i] = vdum[i];
- }
- else {
- gmx_fio_do_rvec(fio,ir->compress[XX]);
- gmx_fio_do_rvec(fio,ir->compress[YY]);
- gmx_fio_do_rvec(fio,ir->compress[ZZ]);
- }
- if (file_version >= 47) {
- gmx_fio_do_int(fio,ir->refcoord_scaling);
- gmx_fio_do_rvec(fio,ir->posres_com);
- gmx_fio_do_rvec(fio,ir->posres_comB);
- } else {
- ir->refcoord_scaling = erscNO;
- clear_rvec(ir->posres_com);
- clear_rvec(ir->posres_comB);
- }
- if(file_version > 25)
- gmx_fio_do_int(fio,ir->andersen_seed);
- else
- ir->andersen_seed=0;
-
- if(file_version < 26) {
- gmx_fio_do_gmx_bool(fio,bSimAnn);
- gmx_fio_do_real(fio,zerotemptime);
- }
-
- if (file_version < 37)
- gmx_fio_do_real(fio,rdum);
-
- gmx_fio_do_real(fio,ir->shake_tol);
- if (file_version < 54)
- gmx_fio_do_real(fio,*fudgeQQ);
- gmx_fio_do_int(fio,ir->efep);
- if (file_version <= 14 && ir->efep > efepNO)
- ir->efep = efepYES;
- if (file_version >= 59) {
- gmx_fio_do_double(fio, ir->init_lambda);
- gmx_fio_do_double(fio, ir->delta_lambda);
- } else {
- gmx_fio_do_real(fio,rdum);
- ir->init_lambda = rdum;
- gmx_fio_do_real(fio,rdum);
- ir->delta_lambda = rdum;
- }
- if (file_version >= 64) {
- gmx_fio_do_int(fio,ir->n_flambda);
- if (bRead) {
- snew(ir->flambda,ir->n_flambda);
- }
- bDum=gmx_fio_ndo_double(fio,ir->flambda,ir->n_flambda);
- } else {
- ir->n_flambda = 0;
- ir->flambda = NULL;
- }
- if (file_version >= 13)
- gmx_fio_do_real(fio,ir->sc_alpha);
- else
- ir->sc_alpha = 0;
- if (file_version >= 38)
- gmx_fio_do_int(fio,ir->sc_power);
- else
- ir->sc_power = 2;
- if (file_version >= 15)
- gmx_fio_do_real(fio,ir->sc_sigma);
- else
- ir->sc_sigma = 0.3;
- if (bRead)
- {
- if (file_version >= 71)
- {
- ir->sc_sigma_min = ir->sc_sigma;
- }
- else
- {
- ir->sc_sigma_min = 0;
- }
- }
- if (file_version >= 64) {
- gmx_fio_do_int(fio,ir->nstdhdl);
- } else {
- ir->nstdhdl = 1;
- }
-
- if (file_version >= 73)
- {
- gmx_fio_do_int(fio, ir->separate_dhdl_file);
- gmx_fio_do_int(fio, ir->dhdl_derivatives);
- }
- else
- {
- ir->separate_dhdl_file = sepdhdlfileYES;
- ir->dhdl_derivatives = dhdlderivativesYES;
- }
-
- if (file_version >= 71)
- {
- gmx_fio_do_int(fio,ir->dh_hist_size);
- gmx_fio_do_double(fio,ir->dh_hist_spacing);
- }
- else
- {
- ir->dh_hist_size = 0;
- ir->dh_hist_spacing = 0.1;
- }
- if (file_version >= 57) {
- gmx_fio_do_int(fio,ir->eDisre);
- }
- gmx_fio_do_int(fio,ir->eDisreWeighting);
- if (file_version < 22) {
- if (ir->eDisreWeighting == 0)
- ir->eDisreWeighting = edrwEqual;
- else
- ir->eDisreWeighting = edrwConservative;
- }
- gmx_fio_do_gmx_bool(fio,ir->bDisreMixed);
- gmx_fio_do_real(fio,ir->dr_fc);
- gmx_fio_do_real(fio,ir->dr_tau);
- gmx_fio_do_int(fio,ir->nstdisreout);
- if (file_version >= 22) {
- gmx_fio_do_real(fio,ir->orires_fc);
- gmx_fio_do_real(fio,ir->orires_tau);
- gmx_fio_do_int(fio,ir->nstorireout);
- } else {
- ir->orires_fc = 0;
- ir->orires_tau = 0;
- ir->nstorireout = 0;
- }
- if(file_version >= 26) {
- gmx_fio_do_real(fio,ir->dihre_fc);
- if (file_version < 56) {
- gmx_fio_do_real(fio,rdum);
- gmx_fio_do_int(fio,idum);
- }
- } else {
- ir->dihre_fc=0;
- }
-
- gmx_fio_do_real(fio,ir->em_stepsize);
- gmx_fio_do_real(fio,ir->em_tol);
- if (file_version >= 22)
- gmx_fio_do_gmx_bool(fio,ir->bShakeSOR);
- else if (bRead)
- ir->bShakeSOR = TRUE;
- if (file_version >= 11)
- gmx_fio_do_int(fio,ir->niter);
- else if (bRead) {
- ir->niter = 25;
- fprintf(stderr,"Note: niter not in run input file, setting it to %d\n",
- ir->niter);
- }
- if (file_version >= 21)
- gmx_fio_do_real(fio,ir->fc_stepsize);
- else
- ir->fc_stepsize = 0;
- gmx_fio_do_int(fio,ir->eConstrAlg);
- gmx_fio_do_int(fio,ir->nProjOrder);
- gmx_fio_do_real(fio,ir->LincsWarnAngle);
- if (file_version <= 14)
- gmx_fio_do_int(fio,idum);
- if (file_version >=26)
- gmx_fio_do_int(fio,ir->nLincsIter);
- else if (bRead) {
- ir->nLincsIter = 1;
- fprintf(stderr,"Note: nLincsIter not in run input file, setting it to %d\n",
- ir->nLincsIter);
- }
- if (file_version < 33)
- gmx_fio_do_real(fio,bd_temp);
- gmx_fio_do_real(fio,ir->bd_fric);
- gmx_fio_do_int(fio,ir->ld_seed);
- if (file_version >= 33) {
- for(i=0; i<DIM; i++)
- gmx_fio_do_rvec(fio,ir->deform[i]);
- } else {
- for(i=0; i<DIM; i++)
- clear_rvec(ir->deform[i]);
- }
- if (file_version >= 14)
- gmx_fio_do_real(fio,ir->cos_accel);
- else if (bRead)
- ir->cos_accel = 0;
- gmx_fio_do_int(fio,ir->userint1);
- gmx_fio_do_int(fio,ir->userint2);
- gmx_fio_do_int(fio,ir->userint3);
- gmx_fio_do_int(fio,ir->userint4);
- gmx_fio_do_real(fio,ir->userreal1);
- gmx_fio_do_real(fio,ir->userreal2);
- gmx_fio_do_real(fio,ir->userreal3);
- gmx_fio_do_real(fio,ir->userreal4);
-
- /* pull stuff */
- if (file_version >= 48) {
- gmx_fio_do_int(fio,ir->ePull);
- if (ir->ePull != epullNO) {
- if (bRead)
- snew(ir->pull,1);
- do_pull(fio, ir->pull,bRead,file_version);
- }
- } else {
- ir->ePull = epullNO;
- }
-
- /* grpopts stuff */
- gmx_fio_do_int(fio,ir->opts.ngtc);
- if (file_version >= 69) {
- gmx_fio_do_int(fio,ir->opts.nhchainlength);
- } else {
- ir->opts.nhchainlength = 1;
- }
- gmx_fio_do_int(fio,ir->opts.ngacc);
- gmx_fio_do_int(fio,ir->opts.ngfrz);
- gmx_fio_do_int(fio,ir->opts.ngener);
-
- if (bRead) {
- snew(ir->opts.nrdf, ir->opts.ngtc);
- snew(ir->opts.ref_t, ir->opts.ngtc);
- snew(ir->opts.annealing, ir->opts.ngtc);
- snew(ir->opts.anneal_npoints, ir->opts.ngtc);
- snew(ir->opts.anneal_time, ir->opts.ngtc);
- snew(ir->opts.anneal_temp, ir->opts.ngtc);
- snew(ir->opts.tau_t, ir->opts.ngtc);
- snew(ir->opts.nFreeze,ir->opts.ngfrz);
- snew(ir->opts.acc, ir->opts.ngacc);
- snew(ir->opts.egp_flags,ir->opts.ngener*ir->opts.ngener);
- }
- if (ir->opts.ngtc > 0) {
- if (bRead && file_version<13) {
- snew(tmp,ir->opts.ngtc);
- bDum=gmx_fio_ndo_int(fio,tmp, ir->opts.ngtc);
- for(i=0; i<ir->opts.ngtc; i++)
- ir->opts.nrdf[i] = tmp[i];
- sfree(tmp);
- } else {
- bDum=gmx_fio_ndo_real(fio,ir->opts.nrdf, ir->opts.ngtc);
- }
- bDum=gmx_fio_ndo_real(fio,ir->opts.ref_t,ir->opts.ngtc);
- bDum=gmx_fio_ndo_real(fio,ir->opts.tau_t,ir->opts.ngtc);
- if (file_version<33 && ir->eI==eiBD) {
- for(i=0; i<ir->opts.ngtc; i++)
- ir->opts.tau_t[i] = bd_temp;
- }
- }
- if (ir->opts.ngfrz > 0)
- bDum=gmx_fio_ndo_ivec(fio,ir->opts.nFreeze,ir->opts.ngfrz);
- if (ir->opts.ngacc > 0)
- gmx_fio_ndo_rvec(fio,ir->opts.acc,ir->opts.ngacc);
- if (file_version >= 12)
- bDum=gmx_fio_ndo_int(fio,ir->opts.egp_flags,
- ir->opts.ngener*ir->opts.ngener);
-
- if(bRead && file_version < 26) {
- for(i=0;i<ir->opts.ngtc;i++) {
- if(bSimAnn) {
- ir->opts.annealing[i] = eannSINGLE;
- ir->opts.anneal_npoints[i] = 2;
- snew(ir->opts.anneal_time[i],2);
- snew(ir->opts.anneal_temp[i],2);
- /* calculate the starting/ending temperatures from reft, zerotemptime, and nsteps */
- finish_t = ir->init_t + ir->nsteps * ir->delta_t;
- init_temp = ir->opts.ref_t[i]*(1-ir->init_t/zerotemptime);
- finish_temp = ir->opts.ref_t[i]*(1-finish_t/zerotemptime);
- ir->opts.anneal_time[i][0] = ir->init_t;
- ir->opts.anneal_time[i][1] = finish_t;
- ir->opts.anneal_temp[i][0] = init_temp;
- ir->opts.anneal_temp[i][1] = finish_temp;
- } else {
- ir->opts.annealing[i] = eannNO;
- ir->opts.anneal_npoints[i] = 0;
- }
- }
- } else {
- /* file version 26 or later */
- /* First read the lists with annealing and npoints for each group */
- bDum=gmx_fio_ndo_int(fio,ir->opts.annealing,ir->opts.ngtc);
- bDum=gmx_fio_ndo_int(fio,ir->opts.anneal_npoints,ir->opts.ngtc);
- for(j=0;j<(ir->opts.ngtc);j++) {
- k=ir->opts.anneal_npoints[j];
- if(bRead) {
- snew(ir->opts.anneal_time[j],k);
- snew(ir->opts.anneal_temp[j],k);
- }
- bDum=gmx_fio_ndo_real(fio,ir->opts.anneal_time[j],k);
- bDum=gmx_fio_ndo_real(fio,ir->opts.anneal_temp[j],k);
- }
- }
- /* Walls */
- if (file_version >= 45) {
- gmx_fio_do_int(fio,ir->nwall);
- gmx_fio_do_int(fio,ir->wall_type);
- if (file_version >= 50)
- gmx_fio_do_real(fio,ir->wall_r_linpot);
- else
- ir->wall_r_linpot = -1;
- gmx_fio_do_int(fio,ir->wall_atomtype[0]);
- gmx_fio_do_int(fio,ir->wall_atomtype[1]);
- gmx_fio_do_real(fio,ir->wall_density[0]);
- gmx_fio_do_real(fio,ir->wall_density[1]);
- gmx_fio_do_real(fio,ir->wall_ewald_zfac);
- } else {
- ir->nwall = 0;
- ir->wall_type = 0;
- ir->wall_atomtype[0] = -1;
- ir->wall_atomtype[1] = -1;
- ir->wall_density[0] = 0;
- ir->wall_density[1] = 0;
- ir->wall_ewald_zfac = 3;
- }
- /* Cosine stuff for electric fields */
- for(j=0; (j<DIM); j++) {
- gmx_fio_do_int(fio,ir->ex[j].n);
- gmx_fio_do_int(fio,ir->et[j].n);
- if (bRead) {
- snew(ir->ex[j].a, ir->ex[j].n);
- snew(ir->ex[j].phi,ir->ex[j].n);
- snew(ir->et[j].a, ir->et[j].n);
- snew(ir->et[j].phi,ir->et[j].n);
- }
- bDum=gmx_fio_ndo_real(fio,ir->ex[j].a, ir->ex[j].n);
- bDum=gmx_fio_ndo_real(fio,ir->ex[j].phi,ir->ex[j].n);
- bDum=gmx_fio_ndo_real(fio,ir->et[j].a, ir->et[j].n);
- bDum=gmx_fio_ndo_real(fio,ir->et[j].phi,ir->et[j].n);
- }
-
- /* QMMM stuff */
- if(file_version>=39){
- gmx_fio_do_gmx_bool(fio,ir->bQMMM);
- gmx_fio_do_int(fio,ir->QMMMscheme);
- gmx_fio_do_real(fio,ir->scalefactor);
- gmx_fio_do_int(fio,ir->opts.ngQM);
- if (bRead) {
- snew(ir->opts.QMmethod, ir->opts.ngQM);
- snew(ir->opts.QMbasis, ir->opts.ngQM);
- snew(ir->opts.QMcharge, ir->opts.ngQM);
- snew(ir->opts.QMmult, ir->opts.ngQM);
- snew(ir->opts.bSH, ir->opts.ngQM);
- snew(ir->opts.CASorbitals, ir->opts.ngQM);
- snew(ir->opts.CASelectrons,ir->opts.ngQM);
- snew(ir->opts.SAon, ir->opts.ngQM);
- snew(ir->opts.SAoff, ir->opts.ngQM);
- snew(ir->opts.SAsteps, ir->opts.ngQM);
- snew(ir->opts.bOPT, ir->opts.ngQM);
- snew(ir->opts.bTS, ir->opts.ngQM);
- }
- if (ir->opts.ngQM > 0) {
- bDum=gmx_fio_ndo_int(fio,ir->opts.QMmethod,ir->opts.ngQM);
- bDum=gmx_fio_ndo_int(fio,ir->opts.QMbasis,ir->opts.ngQM);
- bDum=gmx_fio_ndo_int(fio,ir->opts.QMcharge,ir->opts.ngQM);
- bDum=gmx_fio_ndo_int(fio,ir->opts.QMmult,ir->opts.ngQM);
- bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bSH,ir->opts.ngQM);
- bDum=gmx_fio_ndo_int(fio,ir->opts.CASorbitals,ir->opts.ngQM);
- bDum=gmx_fio_ndo_int(fio,ir->opts.CASelectrons,ir->opts.ngQM);
- bDum=gmx_fio_ndo_real(fio,ir->opts.SAon,ir->opts.ngQM);
- bDum=gmx_fio_ndo_real(fio,ir->opts.SAoff,ir->opts.ngQM);
- bDum=gmx_fio_ndo_int(fio,ir->opts.SAsteps,ir->opts.ngQM);
- bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bOPT,ir->opts.ngQM);
- bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bTS,ir->opts.ngQM);
- }
- /* end of QMMM stuff */
- }
-}
-
-
-static void do_harm(t_fileio *fio, t_iparams *iparams,gmx_bool bRead)
-{
- gmx_fio_do_real(fio,iparams->harmonic.rA);
- gmx_fio_do_real(fio,iparams->harmonic.krA);
- gmx_fio_do_real(fio,iparams->harmonic.rB);
- gmx_fio_do_real(fio,iparams->harmonic.krB);
-}
-
-void do_iparams(t_fileio *fio, t_functype ftype,t_iparams *iparams,
- gmx_bool bRead, int file_version)
-{
- int i;
- gmx_bool bDum;
- real rdum;
-
- if (!bRead)
- gmx_fio_set_comment(fio, interaction_function[ftype].name);
- switch (ftype) {
- case F_ANGLES:
- case F_G96ANGLES:
- case F_BONDS:
- case F_G96BONDS:
- case F_HARMONIC:
- case F_IDIHS:
- do_harm(fio, iparams,bRead);
- if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && bRead) {
- /* Correct incorrect storage of parameters */
- iparams->pdihs.phiB = iparams->pdihs.phiA;
- iparams->pdihs.cpB = iparams->pdihs.cpA;
- }
- break;
- case F_FENEBONDS:
- gmx_fio_do_real(fio,iparams->fene.bm);
- gmx_fio_do_real(fio,iparams->fene.kb);
- break;
- case F_RESTRBONDS:
- gmx_fio_do_real(fio,iparams->restraint.lowA);
- gmx_fio_do_real(fio,iparams->restraint.up1A);
- gmx_fio_do_real(fio,iparams->restraint.up2A);
- gmx_fio_do_real(fio,iparams->restraint.kA);
- gmx_fio_do_real(fio,iparams->restraint.lowB);
- gmx_fio_do_real(fio,iparams->restraint.up1B);
- gmx_fio_do_real(fio,iparams->restraint.up2B);
- gmx_fio_do_real(fio,iparams->restraint.kB);
- break;
- case F_TABBONDS:
- case F_TABBONDSNC:
- case F_TABANGLES:
- case F_TABDIHS:
- gmx_fio_do_real(fio,iparams->tab.kA);
- gmx_fio_do_int(fio,iparams->tab.table);
- gmx_fio_do_real(fio,iparams->tab.kB);
- break;
- case F_CROSS_BOND_BONDS:
- gmx_fio_do_real(fio,iparams->cross_bb.r1e);
- gmx_fio_do_real(fio,iparams->cross_bb.r2e);
- gmx_fio_do_real(fio,iparams->cross_bb.krr);
- break;
- case F_CROSS_BOND_ANGLES:
- gmx_fio_do_real(fio,iparams->cross_ba.r1e);
- gmx_fio_do_real(fio,iparams->cross_ba.r2e);
- gmx_fio_do_real(fio,iparams->cross_ba.r3e);
- gmx_fio_do_real(fio,iparams->cross_ba.krt);
- break;
- case F_UREY_BRADLEY:
- gmx_fio_do_real(fio,iparams->u_b.theta);
- gmx_fio_do_real(fio,iparams->u_b.ktheta);
- gmx_fio_do_real(fio,iparams->u_b.r13);
- gmx_fio_do_real(fio,iparams->u_b.kUB);
- break;
- case F_QUARTIC_ANGLES:
- gmx_fio_do_real(fio,iparams->qangle.theta);
- bDum=gmx_fio_ndo_real(fio,iparams->qangle.c,5);
- break;
- case F_BHAM:
- gmx_fio_do_real(fio,iparams->bham.a);
- gmx_fio_do_real(fio,iparams->bham.b);
- gmx_fio_do_real(fio,iparams->bham.c);
- break;
- case F_MORSE:
- gmx_fio_do_real(fio,iparams->morse.b0);
- gmx_fio_do_real(fio,iparams->morse.cb);
- gmx_fio_do_real(fio,iparams->morse.beta);
- break;
- case F_CUBICBONDS:
- gmx_fio_do_real(fio,iparams->cubic.b0);
- gmx_fio_do_real(fio,iparams->cubic.kb);
- gmx_fio_do_real(fio,iparams->cubic.kcub);
- break;
- case F_CONNBONDS:
- break;
- case F_POLARIZATION:
- gmx_fio_do_real(fio,iparams->polarize.alpha);
- break;
- case F_WATER_POL:
- if (file_version < 31)
- gmx_fatal(FARGS,"Old tpr files with water_polarization not supported. Make a new.");
- gmx_fio_do_real(fio,iparams->wpol.al_x);
- gmx_fio_do_real(fio,iparams->wpol.al_y);
- gmx_fio_do_real(fio,iparams->wpol.al_z);
- gmx_fio_do_real(fio,iparams->wpol.rOH);
- gmx_fio_do_real(fio,iparams->wpol.rHH);
- gmx_fio_do_real(fio,iparams->wpol.rOD);
- break;
- case F_THOLE_POL:
- gmx_fio_do_real(fio,iparams->thole.a);
- gmx_fio_do_real(fio,iparams->thole.alpha1);
- gmx_fio_do_real(fio,iparams->thole.alpha2);
- gmx_fio_do_real(fio,iparams->thole.rfac);
- break;
- case F_LJ:
- gmx_fio_do_real(fio,iparams->lj.c6);
- gmx_fio_do_real(fio,iparams->lj.c12);
- break;
- case F_LJ14:
- gmx_fio_do_real(fio,iparams->lj14.c6A);
- gmx_fio_do_real(fio,iparams->lj14.c12A);
- gmx_fio_do_real(fio,iparams->lj14.c6B);
- gmx_fio_do_real(fio,iparams->lj14.c12B);
- break;
- case F_LJC14_Q:
- gmx_fio_do_real(fio,iparams->ljc14.fqq);
- gmx_fio_do_real(fio,iparams->ljc14.qi);
- gmx_fio_do_real(fio,iparams->ljc14.qj);
- gmx_fio_do_real(fio,iparams->ljc14.c6);
- gmx_fio_do_real(fio,iparams->ljc14.c12);
- break;
- case F_LJC_PAIRS_NB:
- gmx_fio_do_real(fio,iparams->ljcnb.qi);
- gmx_fio_do_real(fio,iparams->ljcnb.qj);
- gmx_fio_do_real(fio,iparams->ljcnb.c6);
- gmx_fio_do_real(fio,iparams->ljcnb.c12);
- break;
- case F_PDIHS:
- case F_PIDIHS:
- case F_ANGRES:
- case F_ANGRESZ:
- gmx_fio_do_real(fio,iparams->pdihs.phiA);
- gmx_fio_do_real(fio,iparams->pdihs.cpA);
- if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && file_version < 42) {
- /* Read the incorrectly stored multiplicity */
- gmx_fio_do_real(fio,iparams->harmonic.rB);
- gmx_fio_do_real(fio,iparams->harmonic.krB);
- iparams->pdihs.phiB = iparams->pdihs.phiA;
- iparams->pdihs.cpB = iparams->pdihs.cpA;
- } else {
- gmx_fio_do_real(fio,iparams->pdihs.phiB);
- gmx_fio_do_real(fio,iparams->pdihs.cpB);
- gmx_fio_do_int(fio,iparams->pdihs.mult);
- }
- break;
- case F_DISRES:
- gmx_fio_do_int(fio,iparams->disres.label);
- gmx_fio_do_int(fio,iparams->disres.type);
- gmx_fio_do_real(fio,iparams->disres.low);
- gmx_fio_do_real(fio,iparams->disres.up1);
- gmx_fio_do_real(fio,iparams->disres.up2);
- gmx_fio_do_real(fio,iparams->disres.kfac);
- break;
- case F_ORIRES:
- gmx_fio_do_int(fio,iparams->orires.ex);
- gmx_fio_do_int(fio,iparams->orires.label);
- gmx_fio_do_int(fio,iparams->orires.power);
- gmx_fio_do_real(fio,iparams->orires.c);
- gmx_fio_do_real(fio,iparams->orires.obs);
- gmx_fio_do_real(fio,iparams->orires.kfac);
- break;
- case F_DIHRES:
- gmx_fio_do_int(fio,iparams->dihres.power);
- gmx_fio_do_int(fio,iparams->dihres.label);
- gmx_fio_do_real(fio,iparams->dihres.phi);
- gmx_fio_do_real(fio,iparams->dihres.dphi);
- gmx_fio_do_real(fio,iparams->dihres.kfac);
- break;
- case F_POSRES:
- gmx_fio_do_rvec(fio,iparams->posres.pos0A);
- gmx_fio_do_rvec(fio,iparams->posres.fcA);
- if (bRead && file_version < 27) {
- copy_rvec(iparams->posres.pos0A,iparams->posres.pos0B);
- copy_rvec(iparams->posres.fcA,iparams->posres.fcB);
- } else {
- gmx_fio_do_rvec(fio,iparams->posres.pos0B);
- gmx_fio_do_rvec(fio,iparams->posres.fcB);
- }
- break;
- case F_RBDIHS:
- bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcA,NR_RBDIHS);
- if(file_version>=25)
- bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcB,NR_RBDIHS);
- break;
- case F_FOURDIHS:
- /* Fourier dihedrals are internally represented
- * as Ryckaert-Bellemans since those are faster to compute.
- */
- bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcA, NR_RBDIHS);
- bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcB, NR_RBDIHS);
- break;
- case F_CONSTR:
- case F_CONSTRNC:
- gmx_fio_do_real(fio,iparams->constr.dA);
- gmx_fio_do_real(fio,iparams->constr.dB);
- break;
- case F_SETTLE:
- gmx_fio_do_real(fio,iparams->settle.doh);
- gmx_fio_do_real(fio,iparams->settle.dhh);
- break;
- case F_VSITE2:
- gmx_fio_do_real(fio,iparams->vsite.a);
- break;
- case F_VSITE3:
- case F_VSITE3FD:
- case F_VSITE3FAD:
- gmx_fio_do_real(fio,iparams->vsite.a);
- gmx_fio_do_real(fio,iparams->vsite.b);
- break;
- case F_VSITE3OUT:
- case F_VSITE4FD:
- case F_VSITE4FDN:
- gmx_fio_do_real(fio,iparams->vsite.a);
- gmx_fio_do_real(fio,iparams->vsite.b);
- gmx_fio_do_real(fio,iparams->vsite.c);
- break;
- case F_VSITEN:
- gmx_fio_do_int(fio,iparams->vsiten.n);
- gmx_fio_do_real(fio,iparams->vsiten.a);
- break;
- case F_GB12:
- case F_GB13:
- case F_GB14:
- /* We got rid of some parameters in version 68 */
- if(bRead && file_version<68)
- {
- gmx_fio_do_real(fio,rdum);
- gmx_fio_do_real(fio,rdum);
- gmx_fio_do_real(fio,rdum);
- gmx_fio_do_real(fio,rdum);
- }
- gmx_fio_do_real(fio,iparams->gb.sar);
- gmx_fio_do_real(fio,iparams->gb.st);
- gmx_fio_do_real(fio,iparams->gb.pi);
- gmx_fio_do_real(fio,iparams->gb.gbr);
- gmx_fio_do_real(fio,iparams->gb.bmlt);
- break;
- case F_CMAP:
- gmx_fio_do_int(fio,iparams->cmap.cmapA);
- gmx_fio_do_int(fio,iparams->cmap.cmapB);
- break;
- default:
- gmx_fatal(FARGS,"unknown function type %d (%s) in %s line %d",
-
- ftype,interaction_function[ftype].name,__FILE__,__LINE__);
- }
- if (!bRead)
- gmx_fio_unset_comment(fio);
-}
-
-static void do_ilist(t_fileio *fio, t_ilist *ilist,gmx_bool bRead,int file_version,
- int ftype)
-{
- int i,k,idum;
- gmx_bool bDum=TRUE;
-
- if (!bRead) {
- gmx_fio_set_comment(fio, interaction_function[ftype].name);
- }
- if (file_version < 44) {
- for(i=0; i<MAXNODES; i++)
- gmx_fio_do_int(fio,idum);
- }
- gmx_fio_do_int(fio,ilist->nr);
- if (bRead)
- snew(ilist->iatoms,ilist->nr);
- bDum=gmx_fio_ndo_int(fio,ilist->iatoms,ilist->nr);
- if (!bRead)
- gmx_fio_unset_comment(fio);
-}
-
-static void do_ffparams(t_fileio *fio, gmx_ffparams_t *ffparams,
- gmx_bool bRead, int file_version)
-{
- int idum,i,j;
- gmx_bool bDum=TRUE;
- unsigned int k;
-
- gmx_fio_do_int(fio,ffparams->atnr);
- if (file_version < 57) {
- gmx_fio_do_int(fio,idum);
- }
- gmx_fio_do_int(fio,ffparams->ntypes);
- if (bRead && debug)
- fprintf(debug,"ffparams->atnr = %d, ntypes = %d\n",
- ffparams->atnr,ffparams->ntypes);
- if (bRead) {
- snew(ffparams->functype,ffparams->ntypes);
- snew(ffparams->iparams,ffparams->ntypes);
- }
- /* Read/write all the function types */
- bDum=gmx_fio_ndo_int(fio,ffparams->functype,ffparams->ntypes);
- if (bRead && debug)
- pr_ivec(debug,0,"functype",ffparams->functype,ffparams->ntypes,TRUE);
-
- if (file_version >= 66) {
- gmx_fio_do_double(fio,ffparams->reppow);
- } else {
- ffparams->reppow = 12.0;
- }
-
- if (file_version >= 57) {
- gmx_fio_do_real(fio,ffparams->fudgeQQ);
- }
-
- /* Check whether all these function types are supported by the code.
- * In practice the code is backwards compatible, which means that the
- * numbering may have to be altered from old numbering to new numbering
- */
- for (i=0; (i<ffparams->ntypes); i++) {
- if (bRead)
- /* Loop over file versions */
- for (k=0; (k<NFTUPD); k++)
- /* Compare the read file_version to the update table */
- if ((file_version < ftupd[k].fvnr) &&
- (ffparams->functype[i] >= ftupd[k].ftype)) {
- ffparams->functype[i] += 1;
- if (debug) {
- fprintf(debug,"Incrementing function type %d to %d (due to %s)\n",
- i,ffparams->functype[i],
- interaction_function[ftupd[k].ftype].longname);
- fflush(debug);
- }
- }
-
- do_iparams(fio, ffparams->functype[i],&ffparams->iparams[i],bRead,
- file_version);
- if (bRead && debug)
- pr_iparams(debug,ffparams->functype[i],&ffparams->iparams[i]);
- }
-}
-
-static void do_ilists(t_fileio *fio, t_ilist *ilist,gmx_bool bRead,
- int file_version)
-{
- int i,j,renum[F_NRE];
- gmx_bool bDum=TRUE,bClear;
- unsigned int k;
-
- for(j=0; (j<F_NRE); j++) {
- bClear = FALSE;
- if (bRead)
- for (k=0; k<NFTUPD; k++)
- if ((file_version < ftupd[k].fvnr) && (j == ftupd[k].ftype))
- bClear = TRUE;
- if (bClear) {
- ilist[j].nr = 0;
- ilist[j].iatoms = NULL;
- } else {
- do_ilist(fio, &ilist[j],bRead,file_version,j);
- }
- /*
- if (bRead && gmx_debug_at)
- pr_ilist(debug,0,interaction_function[j].longname,
- functype,&ilist[j],TRUE);
- */
- }
-}
-
-static void do_idef(t_fileio *fio, gmx_ffparams_t *ffparams,gmx_moltype_t *molt,
- gmx_bool bRead, int file_version)
-{
- do_ffparams(fio, ffparams,bRead,file_version);
-
- if (file_version >= 54) {
- gmx_fio_do_real(fio,ffparams->fudgeQQ);
- }
-
- do_ilists(fio, molt->ilist,bRead,file_version);
-}
-
-static void do_block(t_fileio *fio, t_block *block,gmx_bool bRead,int file_version)
-{
- int i,idum,dum_nra,*dum_a;
- gmx_bool bDum=TRUE;
-
- if (file_version < 44)
- for(i=0; i<MAXNODES; i++)
- gmx_fio_do_int(fio,idum);
- gmx_fio_do_int(fio,block->nr);
- if (file_version < 51)
- gmx_fio_do_int(fio,dum_nra);
- if (bRead) {
- block->nalloc_index = block->nr+1;
- snew(block->index,block->nalloc_index);
- }
- bDum=gmx_fio_ndo_int(fio,block->index,block->nr+1);
-
- if (file_version < 51 && dum_nra > 0) {
- snew(dum_a,dum_nra);
- bDum=gmx_fio_ndo_int(fio,dum_a,dum_nra);
- sfree(dum_a);
- }
-}
-
-static void do_blocka(t_fileio *fio, t_blocka *block,gmx_bool bRead,
- int file_version)
-{
- int i,idum;
- gmx_bool bDum=TRUE;
-
- if (file_version < 44)
- for(i=0; i<MAXNODES; i++)
- gmx_fio_do_int(fio,idum);
- gmx_fio_do_int(fio,block->nr);
- gmx_fio_do_int(fio,block->nra);
- if (bRead) {
- block->nalloc_index = block->nr+1;
- snew(block->index,block->nalloc_index);
- block->nalloc_a = block->nra;
- snew(block->a,block->nalloc_a);
- }
- bDum=gmx_fio_ndo_int(fio,block->index,block->nr+1);
- bDum=gmx_fio_ndo_int(fio,block->a,block->nra);
-}
-
-static void do_atom(t_fileio *fio, t_atom *atom,int ngrp,gmx_bool bRead,
- int file_version, gmx_groups_t *groups,int atnr)
-{
- int i,myngrp;
-
- gmx_fio_do_real(fio,atom->m);
- gmx_fio_do_real(fio,atom->q);
- gmx_fio_do_real(fio,atom->mB);
- gmx_fio_do_real(fio,atom->qB);
- gmx_fio_do_ushort(fio, atom->type);
- gmx_fio_do_ushort(fio, atom->typeB);
- gmx_fio_do_int(fio,atom->ptype);
- gmx_fio_do_int(fio,atom->resind);
- if (file_version >= 52)
- gmx_fio_do_int(fio,atom->atomnumber);
- else if (bRead)
- atom->atomnumber = NOTSET;
- if (file_version < 23)
- myngrp = 8;
- else if (file_version < 39)
- myngrp = 9;
- else
- myngrp = ngrp;
-
- if (file_version < 57) {
- unsigned char uchar[egcNR];
- gmx_fio_ndo_uchar(fio,uchar,myngrp);
- for(i=myngrp; (i<ngrp); i++) {
- uchar[i] = 0;
- }
- /* Copy the old data format to the groups struct */
- for(i=0; i<ngrp; i++) {
- groups->grpnr[i][atnr] = uchar[i];
- }
- }
-}
-
-static void do_grps(t_fileio *fio, int ngrp,t_grps grps[],gmx_bool bRead,
- int file_version)
-{
- int i,j,myngrp;
- gmx_bool bDum=TRUE;
-
- if (file_version < 23)
- myngrp = 8;
- else if (file_version < 39)
- myngrp = 9;
- else
- myngrp = ngrp;
-
- for(j=0; (j<ngrp); j++) {
- if (j<myngrp) {
- gmx_fio_do_int(fio,grps[j].nr);
- if (bRead)
- snew(grps[j].nm_ind,grps[j].nr);
- bDum=gmx_fio_ndo_int(fio,grps[j].nm_ind,grps[j].nr);
- }
- else {
- grps[j].nr = 1;
- snew(grps[j].nm_ind,grps[j].nr);
- }
- }
-}
-
-static void do_symstr(t_fileio *fio, char ***nm,gmx_bool bRead,t_symtab *symtab)
-{
- int ls;
-
- if (bRead) {
- gmx_fio_do_int(fio,ls);
- *nm = get_symtab_handle(symtab,ls);
- }
- else {
- ls = lookup_symtab(symtab,*nm);
- gmx_fio_do_int(fio,ls);
- }
-}
-
-static void do_strstr(t_fileio *fio, int nstr,char ***nm,gmx_bool bRead,
- t_symtab *symtab)
-{
- int j;
-
- for (j=0; (j<nstr); j++)
- do_symstr(fio, &(nm[j]),bRead,symtab);
-}
-
-static void do_resinfo(t_fileio *fio, int n,t_resinfo *ri,gmx_bool bRead,
- t_symtab *symtab, int file_version)
-{
- int j;
-
- for (j=0; (j<n); j++) {
- do_symstr(fio, &(ri[j].name),bRead,symtab);
- if (file_version >= 63) {
- gmx_fio_do_int(fio,ri[j].nr);
- gmx_fio_do_uchar(fio, ri[j].ic);
- } else {
- ri[j].nr = j + 1;
- ri[j].ic = ' ';
- }
- }
-}
-
-static void do_atoms(t_fileio *fio, t_atoms *atoms,gmx_bool bRead,t_symtab *symtab,
- int file_version,
- gmx_groups_t *groups)
-{
- int i;
-
- gmx_fio_do_int(fio,atoms->nr);
- gmx_fio_do_int(fio,atoms->nres);
- if (file_version < 57) {
- gmx_fio_do_int(fio,groups->ngrpname);
- for(i=0; i<egcNR; i++) {
- groups->ngrpnr[i] = atoms->nr;
- snew(groups->grpnr[i],groups->ngrpnr[i]);
- }
- }
- if (bRead) {
- snew(atoms->atom,atoms->nr);
- snew(atoms->atomname,atoms->nr);
- snew(atoms->atomtype,atoms->nr);
- snew(atoms->atomtypeB,atoms->nr);
- snew(atoms->resinfo,atoms->nres);
- if (file_version < 57) {
- snew(groups->grpname,groups->ngrpname);
- }
- atoms->pdbinfo = NULL;
- }
- for(i=0; (i<atoms->nr); i++) {
- do_atom(fio, &atoms->atom[i],egcNR,bRead, file_version,groups,i);
- }
- do_strstr(fio, atoms->nr,atoms->atomname,bRead,symtab);
- if (bRead && (file_version <= 20)) {
- for(i=0; i<atoms->nr; i++) {
- atoms->atomtype[i] = put_symtab(symtab,"?");
- atoms->atomtypeB[i] = put_symtab(symtab,"?");
- }
- } else {
- do_strstr(fio, atoms->nr,atoms->atomtype,bRead,symtab);
- do_strstr(fio, atoms->nr,atoms->atomtypeB,bRead,symtab);
- }
- do_resinfo(fio, atoms->nres,atoms->resinfo,bRead,symtab,file_version);
-
- if (file_version < 57) {
- do_strstr(fio, groups->ngrpname,groups->grpname,bRead,symtab);
-
- do_grps(fio, egcNR,groups->grps,bRead,file_version);
- }
-}
-
-static void do_groups(t_fileio *fio, gmx_groups_t *groups,
- gmx_bool bRead,t_symtab *symtab,
- int file_version)
-{
- int g,n,i;
- gmx_bool bDum=TRUE;
-
- do_grps(fio, egcNR,groups->grps,bRead,file_version);
- gmx_fio_do_int(fio,groups->ngrpname);
- if (bRead) {
- snew(groups->grpname,groups->ngrpname);
- }
- do_strstr(fio, groups->ngrpname,groups->grpname,bRead,symtab);
- for(g=0; g<egcNR; g++) {
- gmx_fio_do_int(fio,groups->ngrpnr[g]);
- if (groups->ngrpnr[g] == 0) {
- if (bRead) {
- groups->grpnr[g] = NULL;
- }
- } else {
- if (bRead) {
- snew(groups->grpnr[g],groups->ngrpnr[g]);
- }
- bDum=gmx_fio_ndo_uchar(fio, groups->grpnr[g],groups->ngrpnr[g]);
- }
- }
-}
-
-static void do_atomtypes(t_fileio *fio, t_atomtypes *atomtypes,gmx_bool bRead,
- t_symtab *symtab,int file_version)
-{
- int i,j;
- gmx_bool bDum = TRUE;
-
- if (file_version > 25) {
- gmx_fio_do_int(fio,atomtypes->nr);
- j=atomtypes->nr;
- if (bRead) {
- snew(atomtypes->radius,j);
- snew(atomtypes->vol,j);
- snew(atomtypes->surftens,j);
- snew(atomtypes->atomnumber,j);
- snew(atomtypes->gb_radius,j);
- snew(atomtypes->S_hct,j);
- }
- bDum=gmx_fio_ndo_real(fio,atomtypes->radius,j);
- bDum=gmx_fio_ndo_real(fio,atomtypes->vol,j);
- bDum=gmx_fio_ndo_real(fio,atomtypes->surftens,j);
- if(file_version >= 40)
- {
- bDum=gmx_fio_ndo_int(fio,atomtypes->atomnumber,j);
- }
- if(file_version >= 60)
- {
- bDum=gmx_fio_ndo_real(fio,atomtypes->gb_radius,j);
- bDum=gmx_fio_ndo_real(fio,atomtypes->S_hct,j);
- }
- } else {
- /* File versions prior to 26 cannot do GBSA,
- * so they dont use this structure
- */
- atomtypes->nr = 0;
- atomtypes->radius = NULL;
- atomtypes->vol = NULL;
- atomtypes->surftens = NULL;
- atomtypes->atomnumber = NULL;
- atomtypes->gb_radius = NULL;
- atomtypes->S_hct = NULL;
- }
-}
-
-static void do_symtab(t_fileio *fio, t_symtab *symtab,gmx_bool bRead)
-{
- int i,nr;
- t_symbuf *symbuf;
- char buf[STRLEN];
-
- gmx_fio_do_int(fio,symtab->nr);
- nr = symtab->nr;
- if (bRead) {
- snew(symtab->symbuf,1);
- symbuf = symtab->symbuf;
- symbuf->bufsize = nr;
- snew(symbuf->buf,nr);
- for (i=0; (i<nr); i++) {
- gmx_fio_do_string(fio,buf);
- symbuf->buf[i]=strdup(buf);
- }
- }
- else {
- symbuf = symtab->symbuf;
- while (symbuf!=NULL) {
- for (i=0; (i<symbuf->bufsize) && (i<nr); i++)
- gmx_fio_do_string(fio,symbuf->buf[i]);
- nr-=i;
- symbuf=symbuf->next;
- }
- if (nr != 0)
- gmx_fatal(FARGS,"nr of symtab strings left: %d",nr);
- }
-}
-
-static void do_cmap(t_fileio *fio, gmx_cmap_t *cmap_grid, gmx_bool bRead)
-{
- int i,j,ngrid,gs,nelem;
-
- gmx_fio_do_int(fio,cmap_grid->ngrid);
- gmx_fio_do_int(fio,cmap_grid->grid_spacing);
-
- ngrid = cmap_grid->ngrid;
- gs = cmap_grid->grid_spacing;
- nelem = gs * gs;
-
- if(bRead)
- {
- snew(cmap_grid->cmapdata,ngrid);
-
- for(i=0;i<cmap_grid->ngrid;i++)
- {
- snew(cmap_grid->cmapdata[i].cmap,4*nelem);
- }
- }
-
- for(i=0;i<cmap_grid->ngrid;i++)
- {
- for(j=0;j<nelem;j++)
- {
- gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4]);
- gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+1]);
- gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+2]);
- gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+3]);
- }
- }
-}
-
-
-void tpx_make_chain_identifiers(t_atoms *atoms,t_block *mols)
-{
- int m,a,a0,a1,r;
- char c,chainid;
- int chainnum;
-
- /* We always assign a new chain number, but save the chain id characters
- * for larger molecules.
- */
-#define CHAIN_MIN_ATOMS 15
-
- chainnum=0;
- chainid='A';
- for(m=0; m<mols->nr; m++)
- {
- a0=mols->index[m];
- a1=mols->index[m+1];
- if ((a1-a0 >= CHAIN_MIN_ATOMS) && (chainid <= 'Z'))
- {
- c=chainid;
- chainid++;
- }
- else
- {
- c=' ';
- }
- for(a=a0; a<a1; a++)
- {
- atoms->resinfo[atoms->atom[a].resind].chainnum = chainnum;
- atoms->resinfo[atoms->atom[a].resind].chainid = c;
- }
- chainnum++;
- }
-
- /* Blank out the chain id if there was only one chain */
- if (chainid == 'B')
- {
- for(r=0; r<atoms->nres; r++)
- {
- atoms->resinfo[r].chainid = ' ';
- }
- }
-}
-
-static void do_moltype(t_fileio *fio, gmx_moltype_t *molt,gmx_bool bRead,
- t_symtab *symtab, int file_version,
- gmx_groups_t *groups)
-{
- int i;
-
- if (file_version >= 57) {
- do_symstr(fio, &(molt->name),bRead,symtab);
- }
-
- do_atoms(fio, &molt->atoms, bRead, symtab, file_version, groups);
-
- if (bRead && gmx_debug_at) {
- pr_atoms(debug,0,"atoms",&molt->atoms,TRUE);
- }
-
- if (file_version >= 57) {
- do_ilists(fio, molt->ilist,bRead,file_version);
-
- do_block(fio, &molt->cgs,bRead,file_version);
- if (bRead && gmx_debug_at) {
- pr_block(debug,0,"cgs",&molt->cgs,TRUE);
- }
- }
-
- /* This used to be in the atoms struct */
- do_blocka(fio, &molt->excls, bRead, file_version);
-}
-
-static void do_molblock(t_fileio *fio, gmx_molblock_t *molb,gmx_bool bRead,
- int file_version)
-{
- int i;
-
- gmx_fio_do_int(fio,molb->type);
- gmx_fio_do_int(fio,molb->nmol);
- gmx_fio_do_int(fio,molb->natoms_mol);
- /* Position restraint coordinates */
- gmx_fio_do_int(fio,molb->nposres_xA);
- if (molb->nposres_xA > 0) {
- if (bRead) {
- snew(molb->posres_xA,molb->nposres_xA);
- }
- gmx_fio_ndo_rvec(fio,molb->posres_xA,molb->nposres_xA);
- }
- gmx_fio_do_int(fio,molb->nposres_xB);
- if (molb->nposres_xB > 0) {
- if (bRead) {
- snew(molb->posres_xB,molb->nposres_xB);
- }
- gmx_fio_ndo_rvec(fio,molb->posres_xB,molb->nposres_xB);
- }
-
-}
-
-static t_block mtop_mols(gmx_mtop_t *mtop)
-{
- int mb,m,a,mol;
- t_block mols;
-
- mols.nr = 0;
- for(mb=0; mb<mtop->nmolblock; mb++) {
- mols.nr += mtop->molblock[mb].nmol;
- }
- mols.nalloc_index = mols.nr + 1;
- snew(mols.index,mols.nalloc_index);
-
- a = 0;
- m = 0;
- mols.index[m] = a;
- for(mb=0; mb<mtop->nmolblock; mb++) {
- for(mol=0; mol<mtop->molblock[mb].nmol; mol++) {
- a += mtop->molblock[mb].natoms_mol;
- m++;
- mols.index[m] = a;
- }
- }
-
- return mols;
-}
-
-static void add_posres_molblock(gmx_mtop_t *mtop)
-{
- t_ilist *il;
- int am,i,mol,a;
- gmx_bool bFE;
- gmx_molblock_t *molb;
- t_iparams *ip;
-
- il = &mtop->moltype[0].ilist[F_POSRES];
- if (il->nr == 0) {
- return;
- }
- am = 0;
- bFE = FALSE;
- for(i=0; i<il->nr; i+=2) {
- ip = &mtop->ffparams.iparams[il->iatoms[i]];
- am = max(am,il->iatoms[i+1]);
- if (ip->posres.pos0B[XX] != ip->posres.pos0A[XX] ||
- ip->posres.pos0B[YY] != ip->posres.pos0A[YY] ||
- ip->posres.pos0B[ZZ] != ip->posres.pos0A[ZZ]) {
- bFE = TRUE;
- }
- }
- /* Make the posres coordinate block end at a molecule end */
- mol = 0;
- while(am >= mtop->mols.index[mol+1]) {
- mol++;
- }
- molb = &mtop->molblock[0];
- molb->nposres_xA = mtop->mols.index[mol+1];
- snew(molb->posres_xA,molb->nposres_xA);
- if (bFE) {
- molb->nposres_xB = molb->nposres_xA;
- snew(molb->posres_xB,molb->nposres_xB);
- } else {
- molb->nposres_xB = 0;
- }
- for(i=0; i<il->nr; i+=2) {
- ip = &mtop->ffparams.iparams[il->iatoms[i]];
- a = il->iatoms[i+1];
- molb->posres_xA[a][XX] = ip->posres.pos0A[XX];
- molb->posres_xA[a][YY] = ip->posres.pos0A[YY];
- molb->posres_xA[a][ZZ] = ip->posres.pos0A[ZZ];
- if (bFE) {
- molb->posres_xB[a][XX] = ip->posres.pos0B[XX];
- molb->posres_xB[a][YY] = ip->posres.pos0B[YY];
- molb->posres_xB[a][ZZ] = ip->posres.pos0B[ZZ];
- }
- }
-}
-
-static void set_disres_npair(gmx_mtop_t *mtop)
-{
- int mt,i,npair;
- t_iparams *ip;
- t_ilist *il;
- t_iatom *a;
-
- ip = mtop->ffparams.iparams;
-
- for(mt=0; mt<mtop->nmoltype; mt++) {
- il = &mtop->moltype[mt].ilist[F_DISRES];
- if (il->nr > 0) {
- a = il->iatoms;
- npair = 0;
- for(i=0; i<il->nr; i+=3) {
- npair++;
- if (i+3 == il->nr || ip[a[i]].disres.label != ip[a[i+3]].disres.label) {
- ip[a[i]].disres.npair = npair;
- npair = 0;
- }
- }
- }
- }
-}
-
-static void do_mtop(t_fileio *fio, gmx_mtop_t *mtop,gmx_bool bRead,
- int file_version)
-{
- int mt,mb,i;
- t_blocka dumb;
-
- if (bRead)
- init_mtop(mtop);
- do_symtab(fio, &(mtop->symtab),bRead);
- if (bRead && debug)
- pr_symtab(debug,0,"symtab",&mtop->symtab);
-
- do_symstr(fio, &(mtop->name),bRead,&(mtop->symtab));
-
- if (file_version >= 57) {
- do_ffparams(fio, &mtop->ffparams,bRead,file_version);
-
- gmx_fio_do_int(fio,mtop->nmoltype);
- } else {
- mtop->nmoltype = 1;
- }
- if (bRead) {
- snew(mtop->moltype,mtop->nmoltype);
- if (file_version < 57) {
- mtop->moltype[0].name = mtop->name;
- }
- }
- for(mt=0; mt<mtop->nmoltype; mt++) {
- do_moltype(fio, &mtop->moltype[mt],bRead,&mtop->symtab,file_version,
- &mtop->groups);
- }
-
- if (file_version >= 57) {
- gmx_fio_do_int(fio,mtop->nmolblock);
- } else {
- mtop->nmolblock = 1;
- }
- if (bRead) {
- snew(mtop->molblock,mtop->nmolblock);
- }
- if (file_version >= 57) {
- for(mb=0; mb<mtop->nmolblock; mb++) {
- do_molblock(fio, &mtop->molblock[mb],bRead,file_version);
- }
- gmx_fio_do_int(fio,mtop->natoms);
- } else {
- mtop->molblock[0].type = 0;
- mtop->molblock[0].nmol = 1;
- mtop->molblock[0].natoms_mol = mtop->moltype[0].atoms.nr;
- mtop->molblock[0].nposres_xA = 0;
- mtop->molblock[0].nposres_xB = 0;
- }
-
- do_atomtypes (fio, &(mtop->atomtypes),bRead,&(mtop->symtab), file_version);
- if (bRead && debug)
- pr_atomtypes(debug,0,"atomtypes",&mtop->atomtypes,TRUE);
-
- if (file_version < 57) {
- /* Debug statements are inside do_idef */
- do_idef (fio, &mtop->ffparams,&mtop->moltype[0],bRead,file_version);
- mtop->natoms = mtop->moltype[0].atoms.nr;
- }
-
- if(file_version >= 65)
- {
- do_cmap(fio, &mtop->ffparams.cmap_grid,bRead);
- }
- else
- {
- mtop->ffparams.cmap_grid.ngrid = 0;
- mtop->ffparams.cmap_grid.grid_spacing = 0.1;
- mtop->ffparams.cmap_grid.cmapdata = NULL;
- }
-
- if (file_version >= 57) {
- do_groups(fio, &mtop->groups,bRead,&(mtop->symtab),file_version);
- }
-
- if (file_version < 57) {
- do_block(fio, &mtop->moltype[0].cgs,bRead,file_version);
- if (bRead && gmx_debug_at) {
- pr_block(debug,0,"cgs",&mtop->moltype[0].cgs,TRUE);
- }
- do_block(fio, &mtop->mols,bRead,file_version);
- /* Add the posres coordinates to the molblock */
- add_posres_molblock(mtop);
- }
- if (bRead) {
- if (file_version >= 57) {
- mtop->mols = mtop_mols(mtop);
- }
- if (gmx_debug_at) {
- pr_block(debug,0,"mols",&mtop->mols,TRUE);
- }
- }
-
- if (file_version < 51) {
- /* Here used to be the shake blocks */
- do_blocka(fio, &dumb,bRead,file_version);
- if (dumb.nr > 0)
- sfree(dumb.index);
- if (dumb.nra > 0)
- sfree(dumb.a);
- }
-
- if (bRead) {
- close_symtab(&(mtop->symtab));
- }
-}
-
-/* If TopOnlyOK is TRUE then we can read even future versions
- * of tpx files, provided the file_generation hasn't changed.
- * If it is FALSE, we need the inputrecord too, and bail out
- * if the file is newer than the program.
- *
- * The version and generation if the topology (see top of this file)
- * are returned in the two last arguments.
- *
- * If possible, we will read the inputrec even when TopOnlyOK is TRUE.
- */
-static void do_tpxheader(t_fileio *fio,gmx_bool bRead,t_tpxheader *tpx,
- gmx_bool TopOnlyOK, int *file_version,
- int *file_generation)
-{
- char buf[STRLEN];
- gmx_bool bDouble;
- int precision;
- int fver,fgen;
- int idum=0;
- real rdum=0;
-
- gmx_fio_checktype(fio);
- gmx_fio_setdebug(fio,bDebugMode());
-
- /* NEW! XDR tpb file */
- precision = sizeof(real);
- if (bRead) {
- gmx_fio_do_string(fio,buf);
- if (strncmp(buf,"VERSION",7))
- gmx_fatal(FARGS,"Can not read file %s,\n"
- " this file is from a Gromacs version which is older than 2.0\n"
- " Make a new one with grompp or use a gro or pdb file, if possible",
- gmx_fio_getname(fio));
- gmx_fio_do_int(fio,precision);
- bDouble = (precision == sizeof(double));
- if ((precision != sizeof(float)) && !bDouble)
- gmx_fatal(FARGS,"Unknown precision in file %s: real is %d bytes "
- "instead of %d or %d",
- gmx_fio_getname(fio),precision,sizeof(float),sizeof(double));
- gmx_fio_setprecision(fio,bDouble);
- fprintf(stderr,"Reading file %s, %s (%s precision)\n",
- gmx_fio_getname(fio),buf,bDouble ? "double" : "single");
- }
- else {
- gmx_fio_write_string(fio,GromacsVersion());
- bDouble = (precision == sizeof(double));
- gmx_fio_setprecision(fio,bDouble);
- gmx_fio_do_int(fio,precision);
- fver = tpx_version;
- fgen = tpx_generation;
- }
-
- /* Check versions! */
- gmx_fio_do_int(fio,fver);
-
- if(fver>=26)
- gmx_fio_do_int(fio,fgen);
- else
- fgen=0;
-
- if(file_version!=NULL)
- *file_version = fver;
- if(file_generation!=NULL)
- *file_generation = fgen;
-
-
- if ((fver <= tpx_incompatible_version) ||
- ((fver > tpx_version) && !TopOnlyOK) ||
- (fgen > tpx_generation))
- gmx_fatal(FARGS,"reading tpx file (%s) version %d with version %d program",
- gmx_fio_getname(fio),fver,tpx_version);
-
- do_section(fio,eitemHEADER,bRead);
- gmx_fio_do_int(fio,tpx->natoms);
- if (fver >= 28)
- gmx_fio_do_int(fio,tpx->ngtc);
- else
- tpx->ngtc = 0;
- if (fver < 62) {
- gmx_fio_do_int(fio,idum);
- gmx_fio_do_real(fio,rdum);
- }
- gmx_fio_do_real(fio,tpx->lambda);
- gmx_fio_do_int(fio,tpx->bIr);
- gmx_fio_do_int(fio,tpx->bTop);
- gmx_fio_do_int(fio,tpx->bX);
- gmx_fio_do_int(fio,tpx->bV);
- gmx_fio_do_int(fio,tpx->bF);
- gmx_fio_do_int(fio,tpx->bBox);
-
- if((fgen > tpx_generation)) {
- /* This can only happen if TopOnlyOK=TRUE */
- tpx->bIr=FALSE;
- }
-}
-
-static int do_tpx(t_fileio *fio, gmx_bool bRead,
- t_inputrec *ir,t_state *state,rvec *f,gmx_mtop_t *mtop,
- gmx_bool bXVallocated)
-{
- t_tpxheader tpx;
- t_inputrec dum_ir;
- gmx_mtop_t dum_top;
- gmx_bool TopOnlyOK,bDum=TRUE;
- int file_version,file_generation;
- int i;
- rvec *xptr,*vptr;
- int ePBC;
- gmx_bool bPeriodicMols;
-
- if (!bRead) {
- tpx.natoms = state->natoms;
- tpx.ngtc = state->ngtc;
- tpx.lambda = state->lambda;
- tpx.bIr = (ir != NULL);
- tpx.bTop = (mtop != NULL);
- tpx.bX = (state->x != NULL);
- tpx.bV = (state->v != NULL);
- tpx.bF = (f != NULL);
- tpx.bBox = TRUE;
- }
-
- TopOnlyOK = (ir==NULL);
-
- do_tpxheader(fio,bRead,&tpx,TopOnlyOK,&file_version,&file_generation);
-
- if (bRead) {
- state->flags = 0;
- state->lambda = tpx.lambda;
- /* The init_state calls initialize the Nose-Hoover xi integrals to zero */
- if (bXVallocated) {
- xptr = state->x;
- vptr = state->v;
- init_state(state,0,tpx.ngtc,0,0); /* nose-hoover chains */ /* eventually, need to add nnhpres here? */
- state->natoms = tpx.natoms;
- state->nalloc = tpx.natoms;
- state->x = xptr;
- state->v = vptr;
- } else {
- init_state(state,tpx.natoms,tpx.ngtc,0,0); /* nose-hoover chains */
- }
- }
-
-#define do_test(fio,b,p) if (bRead && (p!=NULL) && !b) gmx_fatal(FARGS,"No %s in %s",#p,gmx_fio_getname(fio))
-
- do_test(fio,tpx.bBox,state->box);
- do_section(fio,eitemBOX,bRead);
- if (tpx.bBox) {
- gmx_fio_ndo_rvec(fio,state->box,DIM);
- if (file_version >= 51) {
- gmx_fio_ndo_rvec(fio,state->box_rel,DIM);
- } else {
- /* We initialize box_rel after reading the inputrec */
- clear_mat(state->box_rel);
- }
- if (file_version >= 28) {
- gmx_fio_ndo_rvec(fio,state->boxv,DIM);
- if (file_version < 56) {
- matrix mdum;
- gmx_fio_ndo_rvec(fio,mdum,DIM);
- }
- }
- }
-
- if (state->ngtc > 0 && file_version >= 28) {
- real *dumv;
- /*ndo_double(state->nosehoover_xi,state->ngtc,bDum);*/
- /*ndo_double(state->nosehoover_vxi,state->ngtc,bDum);*/
- /*ndo_double(state->therm_integral,state->ngtc,bDum);*/
- snew(dumv,state->ngtc);
- if (file_version < 69) {
- bDum=gmx_fio_ndo_real(fio,dumv,state->ngtc);
- }
- /* These used to be the Berendsen tcoupl_lambda's */
- bDum=gmx_fio_ndo_real(fio,dumv,state->ngtc);
- sfree(dumv);
- }
-
- /* Prior to tpx version 26, the inputrec was here.
- * I moved it to enable partial forward-compatibility
- * for analysis/viewer programs.
- */
- if(file_version<26) {
- do_test(fio,tpx.bIr,ir);
- do_section(fio,eitemIR,bRead);
- if (tpx.bIr) {
- if (ir) {
- do_inputrec(fio, ir,bRead,file_version,
- mtop ? &mtop->ffparams.fudgeQQ : NULL);
- if (bRead && debug)
- pr_inputrec(debug,0,"inputrec",ir,FALSE);
- }
- else {
- do_inputrec(fio, &dum_ir,bRead,file_version,
- mtop ? &mtop->ffparams.fudgeQQ :NULL);
- if (bRead && debug)
- pr_inputrec(debug,0,"inputrec",&dum_ir,FALSE);
- done_inputrec(&dum_ir);
- }
-
- }
- }
-
- do_test(fio,tpx.bTop,mtop);
- do_section(fio,eitemTOP,bRead);
- if (tpx.bTop) {
- if (mtop) {
- do_mtop(fio,mtop,bRead, file_version);
- } else {
- do_mtop(fio,&dum_top,bRead,file_version);
- done_mtop(&dum_top,TRUE);
- }
- }
- do_test(fio,tpx.bX,state->x);
- do_section(fio,eitemX,bRead);
- if (tpx.bX) {
- if (bRead) {
- state->flags |= (1<<estX);
- }
- gmx_fio_ndo_rvec(fio,state->x,state->natoms);
- }
-
- do_test(fio,tpx.bV,state->v);
- do_section(fio,eitemV,bRead);
- if (tpx.bV) {
- if (bRead) {
- state->flags |= (1<<estV);
- }
- gmx_fio_ndo_rvec(fio,state->v,state->natoms);
- }
-
- do_test(fio,tpx.bF,f);
- do_section(fio,eitemF,bRead);
- if (tpx.bF) gmx_fio_ndo_rvec(fio,f,state->natoms);
-
- /* Starting with tpx version 26, we have the inputrec
- * at the end of the file, so we can ignore it
- * if the file is never than the software (but still the
- * same generation - see comments at the top of this file.
- *
- *
- */
- ePBC = -1;
- bPeriodicMols = FALSE;
- if (file_version >= 26) {
- do_test(fio,tpx.bIr,ir);
- do_section(fio,eitemIR,bRead);
- if (tpx.bIr) {
- if (file_version >= 53) {
- /* Removed the pbc info from do_inputrec, since we always want it */
- if (!bRead) {
- ePBC = ir->ePBC;
- bPeriodicMols = ir->bPeriodicMols;
- }
- gmx_fio_do_int(fio,ePBC);
- gmx_fio_do_gmx_bool(fio,bPeriodicMols);
- }
- if (file_generation <= tpx_generation && ir) {
- do_inputrec(fio, ir,bRead,file_version,mtop ? &mtop->ffparams.fudgeQQ : NULL);
- if (bRead && debug)
- pr_inputrec(debug,0,"inputrec",ir,FALSE);
- if (file_version < 51)
- set_box_rel(ir,state);
- if (file_version < 53) {
- ePBC = ir->ePBC;
- bPeriodicMols = ir->bPeriodicMols;
- }
- }
- if (bRead && ir && file_version >= 53) {
- /* We need to do this after do_inputrec, since that initializes ir */
- ir->ePBC = ePBC;
- ir->bPeriodicMols = bPeriodicMols;
- }
- }
- }
-
- if (bRead)
- {
- if (tpx.bIr && ir)
- {
- if (state->ngtc == 0)
- {
- /* Reading old version without tcoupl state data: set it */
- init_gtc_state(state,ir->opts.ngtc,0,ir->opts.nhchainlength);
- }
- if (tpx.bTop && mtop)
- {
- if (file_version < 57)
- {
- if (mtop->moltype[0].ilist[F_DISRES].nr > 0)
- {
- ir->eDisre = edrSimple;
- }
- else
- {
- ir->eDisre = edrNone;
- }
- }
- set_disres_npair(mtop);
- }
- }
-
- if (tpx.bTop && mtop)
- {
- gmx_mtop_finalize(mtop);
- }
-
- if (file_version >= 57)
- {
- char *env;
- int ienv;
- env = getenv("GMX_NOCHARGEGROUPS");
- if (env != NULL)
- {
- sscanf(env,"%d",&ienv);
- fprintf(stderr,"\nFound env.var. GMX_NOCHARGEGROUPS = %d\n",
- ienv);
- if (ienv > 0)
- {
- fprintf(stderr,
- "Will make single atomic charge groups in non-solvent%s\n",
- ienv > 1 ? " and solvent" : "");
- gmx_mtop_make_atomic_charge_groups(mtop,ienv==1);
- }
- fprintf(stderr,"\n");
- }
- }
- }
-
- return ePBC;
-}
-
-/************************************************************
- *
- * The following routines are the exported ones
- *
- ************************************************************/
-
-t_fileio *open_tpx(const char *fn,const char *mode)
-{
- return gmx_fio_open(fn,mode);
-}
-
-void close_tpx(t_fileio *fio)
-{
- gmx_fio_close(fio);
-}
-
-void read_tpxheader(const char *fn, t_tpxheader *tpx, gmx_bool TopOnlyOK,
- int *file_version, int *file_generation)
-{
- t_fileio *fio;
-
- fio = open_tpx(fn,"r");
- do_tpxheader(fio,TRUE,tpx,TopOnlyOK,file_version,file_generation);
- close_tpx(fio);
-}
-
-void write_tpx_state(const char *fn,
- t_inputrec *ir,t_state *state,gmx_mtop_t *mtop)
-{
- t_fileio *fio;
-
- fio = open_tpx(fn,"w");
- do_tpx(fio,FALSE,ir,state,NULL,mtop,FALSE);
- close_tpx(fio);
-}
-
-void read_tpx_state(const char *fn,
- t_inputrec *ir,t_state *state,rvec *f,gmx_mtop_t *mtop)
-{
- t_fileio *fio;
-
- fio = open_tpx(fn,"r");
- do_tpx(fio,TRUE,ir,state,f,mtop,FALSE);
- close_tpx(fio);
-}
-
-int read_tpx(const char *fn,
- t_inputrec *ir, matrix box,int *natoms,
- rvec *x,rvec *v,rvec *f,gmx_mtop_t *mtop)
-{
- t_fileio *fio;
- t_state state;
- int ePBC;
-
- state.x = x;
- state.v = v;
- fio = open_tpx(fn,"r");
- ePBC = do_tpx(fio,TRUE,ir,&state,f,mtop,TRUE);
- close_tpx(fio);
- *natoms = state.natoms;
- if (box)
- copy_mat(state.box,box);
- state.x = NULL;
- state.v = NULL;
- done_state(&state);
-
- return ePBC;
-}
-
-int read_tpx_top(const char *fn,
- t_inputrec *ir, matrix box,int *natoms,
- rvec *x,rvec *v,rvec *f,t_topology *top)
-{
- gmx_mtop_t mtop;
- t_topology *ltop;
- int ePBC;
-
- ePBC = read_tpx(fn,ir,box,natoms,x,v,f,&mtop);
-
- *top = gmx_mtop_t_to_t_topology(&mtop);
-
- return ePBC;
-}
-
-gmx_bool fn2bTPX(const char *file)
-{
- switch (fn2ftp(file)) {
- case efTPR:
- case efTPB:
- case efTPA:
- return TRUE;
- default:
- return FALSE;
- }
-}
-
-gmx_bool read_tps_conf(const char *infile,char *title,t_topology *top,int *ePBC,
- rvec **x,rvec **v,matrix box,gmx_bool bMass)
-{
- t_tpxheader header;
- int natoms,i,version,generation;
- gmx_bool bTop,bXNULL;
- gmx_mtop_t *mtop;
- t_topology *topconv;
- gmx_atomprop_t aps;
-
- bTop = fn2bTPX(infile);
- *ePBC = -1;
- if (bTop) {
- read_tpxheader(infile,&header,TRUE,&version,&generation);
- if (x)
- snew(*x,header.natoms);
- if (v)
- snew(*v,header.natoms);
- snew(mtop,1);
- *ePBC = read_tpx(infile,NULL,box,&natoms,
- (x==NULL) ? NULL : *x,(v==NULL) ? NULL : *v,NULL,mtop);
- *top = gmx_mtop_t_to_t_topology(mtop);
- sfree(mtop);
- strcpy(title,*top->name);
- tpx_make_chain_identifiers(&top->atoms,&top->mols);
- }
- else {
- get_stx_coordnum(infile,&natoms);
- init_t_atoms(&top->atoms,natoms,FALSE);
- bXNULL = (x == NULL);
- snew(*x,natoms);
- if (v)
- snew(*v,natoms);
- read_stx_conf(infile,title,&top->atoms,*x,(v==NULL) ? NULL : *v,ePBC,box);
- if (bXNULL) {
- sfree(*x);
- x = NULL;
- }
- if (bMass) {
- aps = gmx_atomprop_init();
- for(i=0; (i<natoms); i++)
- if (!gmx_atomprop_query(aps,epropMass,
- *top->atoms.resinfo[top->atoms.atom[i].resind].name,
- *top->atoms.atomname[i],
- &(top->atoms.atom[i].m))) {
- if (debug)
- fprintf(debug,"Can not find mass for atom %s %d %s, setting to 1\n",
- *top->atoms.resinfo[top->atoms.atom[i].resind].name,
- top->atoms.resinfo[top->atoms.atom[i].resind].nr,
- *top->atoms.atomname[i]);
- }
- gmx_atomprop_destroy(aps);
- }
- top->idef.ntypes=-1;
- }
-
- return bTop;
-}
+++ /dev/null
-Makefile
-Makefile.in
-.libs
-.deps
+++ /dev/null
-.deps
-.libs
+++ /dev/null
-# Convenience library for common trajectory analysis routines - not installed
-
-AM_CPPFLAGS= -I$(top_srcdir)/include
-
-noinst_LTLIBRARIES = libtrajana.la
-
-libtrajana_la_SOURCES = \
- centerofmass.c displacement.c indexutil.c nbsearch.c \
- poscalc.c position.c trajana.c
-
-CLEANFILES = *.la *~ \\\#*
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in centerofmass.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <typedefs.h>
-#include <pbc.h>
-#include <vec.h>
-
-#include <centerofmass.h>
-
-/*!
- * \param[in] top Topology structure (unused, can be NULL).
- * \param[in] x Position vectors of all atoms.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[out] xout COG position for the indexed atoms.
- * \returns 0 on success.
- */
-int
-gmx_calc_cog(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout)
-{
- int m, j, ai;
-
- clear_rvec(xout);
- for (m = 0; m < nrefat; ++m)
- {
- ai = index[m];
- rvec_inc(xout, x[ai]);
- }
- svmul(1.0/nrefat, xout, xout);
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses.
- * \param[in] x Position vectors of all atoms.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[out] xout COM position for the indexed atoms.
- * \returns 0 on success, EINVAL if \p top is NULL.
- *
- * Works exactly as gmx_calc_cog() with the exception that a center of
- * mass are calculated, and hence a topology with masses is required.
- */
-int
-gmx_calc_com(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout)
-{
- int m, j, ai;
- real mass, mtot;
-
- if (!top)
- {
- gmx_incons("no masses available while mass weighting was requested");
- return EINVAL;
- }
- clear_rvec(xout);
- mtot = 0;
- for (m = 0; m < nrefat; ++m)
- {
- ai = index[m];
- mass = top->atoms.atom[ai].m;
- for (j = 0; j < DIM; ++j)
- {
- xout[j] += mass * x[ai][j];
- }
- mtot += mass;
- }
- svmul(1.0/mtot, xout, xout);
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses.
- * \param[in] f Forces on all atoms.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[out] fout Force on the COG position for the indexed atoms.
- * \returns 0 on success, EINVAL if \p top is NULL.
- *
- * No special function is provided for calculating the force on the center of
- * mass, because this can be achieved with gmx_calc_cog().
- */
-int
-gmx_calc_cog_f(t_topology *top, rvec f[], int nrefat, atom_id index[], rvec fout)
-{
- int m, j, ai;
- real mass, mtot;
-
- if (!top)
- {
- gmx_incons("no masses available while mass weighting was needed");
- return EINVAL;
- }
- clear_rvec(fout);
- mtot = 0;
- for (m = 0; m < nrefat; ++m)
- {
- ai = index[m];
- mass = top->atoms.atom[ai].m;
- for (j = 0; j < DIM; ++j)
- {
- fout[j] += f[ai][j] / mass;
- }
- mtot += mass;
- }
- svmul(mtot, fout, fout);
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==FALSE).
- * \param[in] x Position vectors of all atoms.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[in] bMass If TRUE, mass weighting is used.
- * \param[out] xout COM/COG position for the indexed atoms.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
- *
- * Calls either gmx_calc_com() or gmx_calc_cog() depending on the value of
- * \p bMass.
- * Other parameters are passed unmodified to these functions.
- */
-int
-gmx_calc_comg(t_topology *top, rvec x[], int nrefat, atom_id index[],
- gmx_bool bMass, rvec xout)
-{
- if (bMass)
- {
- return gmx_calc_com(top, x, nrefat, index, xout);
- }
- else
- {
- return gmx_calc_cog(top, x, nrefat, index, xout);
- }
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==TRUE).
- * \param[in] x Forces on all atoms.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[in] bMass If TRUE, force on COM is calculated.
- * \param[out] xout Force on the COM/COG position for the indexed atoms.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is FALSE.
- *
- * Calls either gmx_calc_cog() or gmx_calc_cog_f() depending on the value of
- * \p bMass.
- * Other parameters are passed unmodified to these functions.
- */
-int
-gmx_calc_comg_f(t_topology *top, rvec f[], int nrefat, atom_id index[],
- gmx_bool bMass, rvec fout)
-{
- if (bMass)
- {
- return gmx_calc_cog(top, f, nrefat, index, fout);
- }
- else
- {
- return gmx_calc_cog_f(top, f, nrefat, index, fout);
- }
-}
-
-
-/*!
- * \param[in] top Topology structure (unused, can be NULL).
- * \param[in] x Position vectors of all atoms.
- * \param[in] pbc Periodic boundary conditions structure.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[out] xout COG position for the indexed atoms.
- * \returns 0 on success.
- *
- * Works exactly as gmx_calc_com_pbc(), but calculates the center of geometry.
- */
-int
-gmx_calc_cog_pbc(t_topology *top, rvec x[], t_pbc *pbc,
- int nrefat, atom_id index[], rvec xout)
-{
- const real tol = 1e-4;
- gmx_bool bChanged;
- int m, j, ai, iter;
- rvec dx, xtest;
-
- /* First simple calculation */
- gmx_calc_cog(top, x, nrefat, index, xout);
- /* Now check if any atom is more than half the box from the COM */
- if (pbc)
- {
- iter = 0;
- do
- {
- bChanged = FALSE;
- for (m = 0; m < nrefat; ++m)
- {
- ai = index[m];
- pbc_dx(pbc, x[ai], xout, dx);
- rvec_add(xout, dx, xtest);
- for (j = 0; j < DIM; ++j)
- {
- if (fabs(xtest[j] - x[ai][j]) > tol)
- {
- /* Here we have used the wrong image for contributing to the COM */
- xout[j] += (xtest[j] - x[ai][j]) / nrefat;
- x[ai][j] = xtest[j];
- bChanged = TRUE;
- }
- }
- }
- iter++;
- }
- while (bChanged);
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses.
- * \param[in] x Position vectors of all atoms.
- * \param[in] pbc Periodic boundary conditions structure.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[out] xout COM position for the indexed atoms.
- * \returns 0 on success, EINVAL if \p top is NULL.
- *
- * Works as gmx_calc_com(), but takes into account periodic boundary
- * conditions: If any atom is more than half the box from the COM,
- * it is wrapped around and a new COM is calculated. This is repeated
- * until no atoms violate the condition.
- *
- * Modified from src/tools/gmx_sorient.c in Gromacs distribution.
- */
-int
-gmx_calc_com_pbc(t_topology *top, rvec x[], t_pbc *pbc,
- int nrefat, atom_id index[], rvec xout)
-{
- const real tol = 1e-4;
- gmx_bool bChanged;
- int m, j, ai, iter;
- real mass, mtot;
- rvec dx, xtest;
-
- if (!top)
- {
- gmx_incons("no masses available while mass weighting was requested");
- return EINVAL;
- }
- /* First simple calculation */
- clear_rvec(xout);
- mtot = 0;
- for (m = 0; m < nrefat; ++m)
- {
- ai = index[m];
- mass = top->atoms.atom[ai].m;
- for (j = 0; j < DIM; ++j)
- {
- xout[j] += mass * x[ai][j];
- }
- mtot += mass;
- }
- svmul(1.0/mtot, xout, xout);
- /* Now check if any atom is more than half the box from the COM */
- if (pbc)
- {
- iter = 0;
- do
- {
- bChanged = FALSE;
- for (m = 0; m < nrefat; ++m)
- {
- ai = index[m];
- mass = top->atoms.atom[ai].m / mtot;
- pbc_dx(pbc, x[ai], xout, dx);
- rvec_add(xout, dx, xtest);
- for (j = 0; j < DIM; ++j)
- {
- if (fabs(xtest[j] - x[ai][j]) > tol)
- {
- /* Here we have used the wrong image for contributing to the COM */
- xout[j] += mass * (xtest[j] - x[ai][j]);
- x[ai][j] = xtest[j];
- bChanged = TRUE;
- }
- }
- }
- iter++;
- }
- while (bChanged);
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==FALSE).
- * \param[in] x Position vectors of all atoms.
- * \param[in] pbc Periodic boundary conditions structure.
- * \param[in] nrefat Number of atoms in the index.
- * \param[in] index Indices of atoms.
- * \param[in] bMass If TRUE, mass weighting is used.
- * \param[out] xout COM/COG position for the indexed atoms.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
- *
- * Calls either gmx_calc_com() or gmx_calc_cog() depending on the value of
- * \p bMass.
- * Other parameters are passed unmodified to these functions.
- */
-int
-gmx_calc_comg_pbc(t_topology *top, rvec x[], t_pbc *pbc,
- int nrefat, atom_id index[], gmx_bool bMass, rvec xout)
-{
- if (bMass)
- {
- return gmx_calc_com_pbc(top, x, pbc, nrefat, index, xout);
- }
- else
- {
- return gmx_calc_cog_pbc(top, x, pbc, nrefat, index, xout);
- }
-}
-
-
-/*!
- * \param[in] top Topology structure (unused, can be NULL).
- * \param[in] x Position vectors of all atoms.
- * \param[in] block t_block structure that divides \p index into blocks.
- * \param[in] index Indices of atoms.
- * \param[out] xout \p block->nr COG positions.
- * \returns 0 on success.
- */
-int
-gmx_calc_cog_block(t_topology *top, rvec x[], t_block *block, atom_id index[],
- rvec xout[])
-{
- int b, i, ai;
- rvec xb;
-
- for (b = 0; b < block->nr; ++b)
- {
- clear_rvec(xb);
- for (i = block->index[b]; i < block->index[b+1]; ++i)
- {
- ai = index[i];
- rvec_inc(xb, x[ai]);
- }
- svmul(1.0/(block->index[b+1] - block->index[b]), xb, xout[b]);
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses.
- * \param[in] x Position vectors of all atoms.
- * \param[in] block t_block structure that divides \p index into blocks.
- * \param[in] index Indices of atoms.
- * \param[out] xout \p block->nr COM positions.
- * \returns 0 on success, EINVAL if \p top is NULL.
- *
- * Works exactly as gmx_calc_cog_block() with the exception that centers of
- * mass are calculated, and hence a topology with masses is required.
- */
-int
-gmx_calc_com_block(t_topology *top, rvec x[], t_block *block, atom_id index[],
- rvec xout[])
-{
- int b, i, ai, d;
- rvec xb;
- real mass, mtot;
-
- if (!top)
- {
- gmx_incons("no masses available while mass weighting was requested");
- return EINVAL;
- }
- for (b = 0; b < block->nr; ++b)
- {
- clear_rvec(xb);
- mtot = 0;
- for (i = block->index[b]; i < block->index[b+1]; ++i)
- {
- ai = index[i];
- mass = top->atoms.atom[ai].m;
- for (d = 0; d < DIM; ++d)
- {
- xb[d] += mass * x[ai][d];
- }
- mtot += mass;
- }
- svmul(1.0/mtot, xb, xout[b]);
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses.
- * \param[in] f Forces on all atoms.
- * \param[in] block t_block structure that divides \p index into blocks.
- * \param[in] index Indices of atoms.
- * \param[out] xout \p block->nr Forces on COG positions.
- * \returns 0 on success, EINVAL if \p top is NULL.
- */
-int
-gmx_calc_cog_f_block(t_topology *top, rvec f[], t_block *block, atom_id index[],
- rvec fout[])
-{
- int b, i, ai, d;
- rvec fb;
- real mass, mtot;
-
- if (!top)
- {
- gmx_incons("no masses available while mass weighting was needed");
- return EINVAL;
- }
- for (b = 0; b < block->nr; ++b)
- {
- clear_rvec(fb);
- mtot = 0;
- for (i = block->index[b]; i < block->index[b+1]; ++i)
- {
- ai = index[i];
- mass = top->atoms.atom[ai].m;
- for (d = 0; d < DIM; ++d)
- {
- fb[d] += f[ai][d] / mass;
- }
- mtot += mass;
- }
- svmul(mtot, fb, fout[b]);
- }
- return 0;
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==FALSE).
- * \param[in] x Position vectors of all atoms.
- * \param[in] block t_block structure that divides \p index into blocks.
- * \param[in] index Indices of atoms.
- * \param[in] bMass If TRUE, mass weighting is used.
- * \param[out] xout \p block->nr COM/COG positions.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
- *
- * Calls either gmx_calc_com_block() or gmx_calc_cog_block() depending on the
- * value of \p bMass.
- * Other parameters are passed unmodified to these functions.
- */
-int
-gmx_calc_comg_block(t_topology *top, rvec x[], t_block *block, atom_id index[],
- gmx_bool bMass, rvec xout[])
-{
- if (bMass)
- {
- return gmx_calc_com_block(top, x, block, index, xout);
- }
- else
- {
- return gmx_calc_cog_block(top, x, block, index, xout);
- }
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==FALSE).
- * \param[in] f Forces on all atoms.
- * \param[in] block t_block structure that divides \p index into blocks.
- * \param[in] index Indices of atoms.
- * \param[in] bMass If TRUE, force on COM is calculated.
- * \param[out] xout \p block->nr forces on the COM/COG positions.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
- *
- * Calls either gmx_calc_com_block() or gmx_calc_cog_block() depending on the
- * value of \p bMass.
- * Other parameters are passed unmodified to these functions.
- */
-int
-gmx_calc_comg_f_block(t_topology *top, rvec f[], t_block *block, atom_id index[],
- gmx_bool bMass, rvec fout[])
-{
- if (bMass)
- {
- return gmx_calc_cog_block(top, f, block, index, fout);
- }
- else
- {
- return gmx_calc_cog_f_block(top, f, block, index, fout);
- }
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==FALSE).
- * \param[in] x Position vectors of all atoms.
- * \param[in] block Blocks for calculation.
- * \param[in] bMass If TRUE, mass weighting is used.
- * \param[out] xout \p block->nr COM/COG positions.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
- *
- * Calls gmx_calc_comg_block(), converting the \p t_blocka structure into
- * a \p t_block and an index. Other parameters are passed unmodified.
- *
- * \attention
- * This function assumes that a pointer to \c t_blocka can be safely typecast
- * into \c t_block such that the index fields can still be referenced.
- * With the present Gromacs defitions of these types, this is the case,
- * but if the layout of these structures is changed, this may lead to strange
- * crashes.
- */
-int
-gmx_calc_comg_blocka(t_topology *top, rvec x[], t_blocka *block,
- gmx_bool bMass, rvec xout[])
-{
- /* TODO: It would probably be better to do this without the type cast */
- return gmx_calc_comg_block(top, x, (t_block *)block, block->a, bMass, xout);
-}
-
-/*!
- * \param[in] top Topology structure with masses
- * (can be NULL if \p bMASS==TRUE).
- * \param[in] f Forces on all atoms.
- * \param[in] block Blocks for calculation.
- * \param[in] bMass If TRUE, force on COM is calculated.
- * \param[out] fout \p block->nr forces on the COM/COG positions.
- * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is FALSE.
- *
- * Calls gmx_calc_comg_f_block(), converting the \p t_blocka structure into
- * a \p t_block and an index. Other parameters are passed unmodified.
- *
- * \attention
- * This function assumes that a pointer to \c t_blocka can be safely typecast
- * into \c t_block such that the index fields can still be referenced.
- * With the present Gromacs defitions of these types, this is the case,
- * but if the layout of these structures is changed, this may lead to strange
- * crashes.
- */
-int
-gmx_calc_comg_f_blocka(t_topology *top, rvec f[], t_blocka *block,
- gmx_bool bMass, rvec fout[])
-{
- /* TODO: It would probably be better to do this without the type cast */
- return gmx_calc_comg_f_block(top, f, (t_block *)block, block->a, bMass, fout);
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in indexutil.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <index.h>
-#include <smalloc.h>
-#include <string2.h>
-#include <typedefs.h>
-#include <gmx_fatal.h>
-
-#include <indexutil.h>
-
-/********************************************************************
- * gmx_ana_indexgrps_t functions
- ********************************************************************/
-
-/*! \internal \brief
- * Stores a set of index groups.
- */
-struct gmx_ana_indexgrps_t
-{
- /** Number of index groups. */
- int nr;
- /** Array of index groups. */
- gmx_ana_index_t *g;
-};
-
-/*!
- * \param[out] g Index group structure.
- * \param[in] ngrps Number of groups for which memory is allocated.
- */
-void
-gmx_ana_indexgrps_alloc(gmx_ana_indexgrps_t **g, int ngrps)
-{
- snew(*g, 1);
- (*g)->nr = ngrps;
- snew((*g)->g, ngrps);
-}
-
-/*!
- * \param[out] g Index group structure.
- * \param[in] ngrps Number of index groups.
- * \param[in] isize Array of index group sizes.
- * \param[in] index Array of pointers to indices of each group.
- * \param[in] name Array of names of the groups.
- * \param[in] bFree If TRUE, the \p isize, \p index and \p name arrays
- * are freed after they have been copied.
- */
-void
-gmx_ana_indexgrps_set(gmx_ana_indexgrps_t **g, int ngrps, int *isize,
- atom_id **index, char **name, gmx_bool bFree)
-{
- int i;
-
- gmx_ana_indexgrps_alloc(g, ngrps);
- for (i = 0; i < ngrps; ++i)
- {
- gmx_ana_index_set(&(*g)->g[i], isize[i], index[i], name[i], isize[i]);
- }
- if (bFree)
- {
- sfree(isize);
- sfree(index);
- sfree(name);
- }
-}
-
-/*!
- * \param[out] g Index group structure.
- * \param[in] top Topology structure.
- * \param[in] fnm File name for the index file.
- * Memory is automatically allocated.
- *
- * One or both of \p top or \p fnm can be NULL.
- * If \p top is NULL, an index file is required and the groups are read
- * from the file (uses Gromacs routine init_index()).
- * If \p fnm is NULL, default groups are constructed based on the
- * topology (uses Gromacs routine analyse()).
- * If both are null, the index group structure is initialized empty.
- */
-void
-gmx_ana_indexgrps_init(gmx_ana_indexgrps_t **g, t_topology *top,
- const char *fnm)
-{
- t_blocka *block = NULL;
- char **names = NULL;
- int i, j;
-
- if (fnm)
- {
- block = init_index(fnm, &names);
- }
- else if (top)
- {
- block = new_blocka();
- analyse(&top->atoms, block, &names, FALSE, FALSE);
- }
- else
- {
- snew(*g, 1);
- (*g)->nr = 0;
- (*g)->g = NULL;
- return;
- }
-
- gmx_ana_indexgrps_alloc(g, block->nr);
- for (i = 0; i < block->nr; ++i)
- {
- gmx_ana_index_t *grp = &(*g)->g[i];
-
- grp->isize = block->index[i+1] - block->index[i];
- snew(grp->index, grp->isize);
- for (j = 0; j < grp->isize; ++j)
- {
- grp->index[j] = block->a[block->index[i]+j];
- }
- grp->name = names[i];
- grp->nalloc_index = grp->isize;
- }
-
- done_blocka(block);
- sfree(block);
- sfree(names);
-}
-
-/*!
- * \param[out] g Index group structure.
- * \param[in] top Topology structure.
- * \param[in] fnm File name for the index file.
- * \param[in] ngrps Number of required groups.
- * Memory is automatically allocated.
- *
- * One of \p top or \p fnm can be NULL, but not both.
- * If \p top is NULL, an index file is required and the groups are read
- * from the file (uses Gromacs routine rd_index()).
- * If \p fnm is NULL, default groups are constructed based on the
- * topology (uses Gromacs routine get_index()).
- */
-void
-gmx_ana_indexgrps_get(gmx_ana_indexgrps_t **g, t_topology *top,
- const char *fnm, int ngrps)
-{
- int *isize;
- atom_id **index;
- char **name;
-
- snew(isize, ngrps);
- snew(index, ngrps);
- snew(name, ngrps);
- if (!top)
- {
- rd_index(fnm, ngrps, isize, index, name);
- }
- else
- {
- get_index(&(top->atoms), fnm, ngrps, isize, index, name);
- }
- gmx_ana_indexgrps_set(g, ngrps, isize, index, name, TRUE);
-}
-
-/*!
- * \param[out] g Index group structure.
- * \param[in] fnm File name for the index file.
- * \param[in] ngrps Number of required groups.
- * Memory is automatically allocated.
- *
- * This is a convenience function for calling the Gromacs routine
- * rd_index().
- */
-void
-gmx_ana_indexgrps_rd(gmx_ana_indexgrps_t **g, const char *fnm, int ngrps)
-{
- gmx_ana_indexgrps_get(g, NULL, fnm, ngrps);
-}
-
-/*!
- * \param[in] g Index groups structure.
- *
- * The pointer \p g is invalid after the call.
- */
-void
-gmx_ana_indexgrps_free(gmx_ana_indexgrps_t *g)
-{
- int i;
-
- if (g->nr == 0)
- {
- sfree(g);
- return;
- }
- for (i = 0; i < g->nr; ++i)
- {
- gmx_ana_index_deinit(&g->g[i]);
- }
- sfree(g->g);
- g->nr = 0;
- g->g = NULL;
- sfree(g);
-}
-
-/*!
- * \param[out] dest Destination index groups.
- * \param[in] src Source index groups.
- *
- * A deep copy is made for all fields, including the group names.
- */
-void
-gmx_ana_indexgrps_clone(gmx_ana_indexgrps_t **dest, gmx_ana_indexgrps_t *src)
-{
- int g;
-
- gmx_ana_indexgrps_alloc(dest, src->nr);
- for (g = 0; g < src->nr; ++g)
- {
- gmx_ana_index_copy(&(*dest)->g[g], &src->g[g], TRUE);
- }
-}
-
-/*!
- * \param[out] g Index group structure.
- * \returns TRUE if \p g is empty, i.e., has 0 index groups.
- */
-gmx_bool
-gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t *g)
-{
- return g->nr == 0;
-}
-
-/*!
- * \param[in] g Index groups structure.
- * \param[in] n Index group number to get.
- * \returns Pointer to the \p n'th index group in \p g.
- *
- * The returned pointer should not be freed.
- */
-gmx_ana_index_t *
-gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t *g, int n)
-{
- if (n < 0 || n >= g->nr)
- {
- return NULL;
- }
- return &g->g[n];
-}
-
-/*!
- * \param[out] dest Output structure.
- * \param[in] src Input index groups.
- * \param[in] n Number of the group to extract.
- * \returns TRUE if \p n is a valid group in \p src, FALSE otherwise.
- */
-gmx_bool
-gmx_ana_indexgrps_extract(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, int n)
-{
- if (n < 0 || n >= src->nr)
- {
- dest->isize = 0;
- return FALSE;
- }
-
- gmx_ana_index_copy(dest, &src->g[n], TRUE);
- return TRUE;
-}
-
-/*!
- * \param[out] dest Output structure.
- * \param[in] src Input index groups.
- * \param[in] name Name (or part of the name) of the group to extract.
- * \returns TRUE if \p name is a valid group in \p src, FALSE otherwise.
- *
- * Uses the Gromacs routine find_group() to find the actual group;
- * the comparison is case-insensitive.
- */
-gmx_bool
-gmx_ana_indexgrps_find(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, char *name)
-{
- int i;
- char **names;
-
- snew(names, src->nr);
- for (i = 0; i < src->nr; ++i)
- {
- names[i] = src->g[i].name;
- }
- i = find_group(name, src->nr, names);
- sfree(names);
- if (i == NOTSET)
- {
- dest->isize = 0;
- return FALSE;
- }
-
- return gmx_ana_indexgrps_extract(dest, src, i);
-}
-
-/*!
- * \param[in] g Index groups to print.
- * \param[in] maxn Maximum number of indices to print
- * (-1 = print all, 0 = print only names).
- */
-void
-gmx_ana_indexgrps_print(gmx_ana_indexgrps_t *g, int maxn)
-{
- int i;
-
- for (i = 0; i < g->nr; ++i)
- {
- fprintf(stderr, " %2d: ", i);
- gmx_ana_index_dump(&g->g[i], i, maxn);
- }
-}
-
-/********************************************************************
- * gmx_ana_index_t functions
- ********************************************************************/
-
-/*!
- * \param[in,out] g Index group structure.
- * \param[in] isize Maximum number of atoms to reserve space for.
- */
-void
-gmx_ana_index_reserve(gmx_ana_index_t *g, int isize)
-{
- if (g->nalloc_index < isize)
- {
- srenew(g->index, isize);
- g->nalloc_index = isize;
- }
-}
-
-/*!
- * \param[in,out] g Index group structure.
- *
- * Resizes the memory allocated for holding the indices such that the
- * current contents fit.
- */
-void
-gmx_ana_index_squeeze(gmx_ana_index_t *g)
-{
- srenew(g->index, g->isize);
- g->nalloc_index = g->isize;
-}
-
-/*!
- * \param[out] g Output structure.
- *
- * Any contents of \p g are discarded without freeing.
- */
-void
-gmx_ana_index_clear(gmx_ana_index_t *g)
-{
- g->isize = 0;
- g->index = NULL;
- g->name = NULL;
- g->nalloc_index = 0;
-}
-
-/*!
- * \param[out] g Output structure.
- * \param[in] isize Number of atoms in the new group.
- * \param[in] index Array of \p isize atoms (can be NULL if \p isize is 0).
- * \param[in] name Name for the new group (can be NULL).
- * \param[in] nalloc Number of elements allocated for \p index
- * (if 0, \p index is not freed in gmx_ana_index_deinit())
- *
- * No copy if \p index is made.
- */
-void
-gmx_ana_index_set(gmx_ana_index_t *g, int isize, atom_id *index, char *name,
- int nalloc)
-{
- g->isize = isize;
- g->index = index;
- g->name = name;
- g->nalloc_index = nalloc;
-}
-
-/*!
- * \param[out] g Output structure.
- * \param[in] natoms Number of atoms.
- * \param[in] name Name for the new group (can be NULL).
- */
-void
-gmx_ana_index_init_simple(gmx_ana_index_t *g, int natoms, char *name)
-{
- int i;
-
- g->isize = natoms;
- snew(g->index, natoms);
- for (i = 0; i < natoms; ++i)
- {
- g->index[i] = i;
- }
- g->name = name;
- g->nalloc_index = natoms;
-}
-
-/*!
- * \param[in] g Index group structure.
- *
- * The pointer \p g is not freed.
- */
-void
-gmx_ana_index_deinit(gmx_ana_index_t *g)
-{
- if (g->nalloc_index > 0)
- {
- sfree(g->index);
- }
- sfree(g->name);
- gmx_ana_index_clear(g);
-}
-
-/*!
- * \param[out] dest Destination index group.
- * \param[in] src Source index group.
- * \param[in] bAlloc If TRUE, memory is allocated at \p dest; otherwise,
- * it is assumed that enough memory has been allocated for index.
- *
- * A deep copy of the name is only made if \p bAlloc is TRUE.
- */
-void
-gmx_ana_index_copy(gmx_ana_index_t *dest, gmx_ana_index_t *src, gmx_bool bAlloc)
-{
- dest->isize = src->isize;
- if (dest->isize > 0)
- {
- if (bAlloc)
- {
- snew(dest->index, dest->isize);
- dest->nalloc_index = dest->isize;
- }
- memcpy(dest->index, src->index, dest->isize*sizeof(*dest->index));
- }
- if (bAlloc && src->name)
- {
- dest->name = strdup(src->name);
- }
- else if (bAlloc || src->name)
- {
- dest->name = src->name;
- }
-}
-
-/*!
- * \param[in] g Index group to print.
- * \param[in] i Group number to use if the name is NULL.
- * \param[in] maxn Maximum number of indices to print (-1 = print all).
- */
-void
-gmx_ana_index_dump(gmx_ana_index_t *g, int i, int maxn)
-{
- int j, n;
-
- if (g->name)
- {
- fprintf(stderr, "\"%s\"", g->name);
- }
- else
- {
- fprintf(stderr, "Group %d", i+1);
- }
- fprintf(stderr, " (%d atoms)", g->isize);
- if (maxn != 0)
- {
- fprintf(stderr, ":");
- n = g->isize;
- if (maxn >= 0 && n > maxn)
- {
- n = maxn;
- }
- for (j = 0; j < n; ++j)
- {
- fprintf(stderr, " %d", g->index[j]+1);
- }
- if (n < g->isize)
- {
- fprintf(stderr, " ...");
- }
- }
- fprintf(stderr, "\n");
-}
-
-/*!
- * \param[in] g Input index group.
- * \param[in] natoms Number of atoms to check against.
- *
- * If any atom index in the index group is less than zero or >= \p natoms,
- * gmx_fatal() is called.
- */
-void
-gmx_ana_index_check(gmx_ana_index_t *g, int natoms)
-{
- int j;
-
- for (j = 0; j < g->isize; ++j)
- {
- if (g->index[j] >= natoms)
- {
- gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) "
- "larger than number of atoms in trajectory (%d atoms)",
- g->index[j], g->name, g->isize, natoms);
- }
- else if (g->index[j] < 0)
- {
- gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) "
- "is less than zero",
- g->index[j], g->name, g->isize);
- }
- }
-}
-
-/*!
- * \param[in] g Index group to check.
- * \returns TRUE if the index group is sorted and has no duplicates,
- * FALSE otherwise.
- */
-gmx_bool
-gmx_ana_index_check_sorted(gmx_ana_index_t *g)
-{
- int i;
-
- for (i = 0; i < g->isize-1; ++i)
- {
- if (g->index[i+1] <= g->index[i])
- {
- return FALSE;
- }
- }
- return TRUE;
-}
-
-/********************************************************************
- * Set operations
- ********************************************************************/
-
-/** Helper function for gmx_ana_index_sort(). */
-static int
-cmp_atomid(const void *a, const void *b)
-{
- if (*(atom_id *)a < *(atom_id *)b) return -1;
- if (*(atom_id *)a > *(atom_id *)b) return 1;
- return 0;
-}
-
-/*!
- * \param[in,out] g Index group to be sorted.
- */
-void
-gmx_ana_index_sort(gmx_ana_index_t *g)
-{
- qsort(g->index, g->isize, sizeof(*g->index), cmp_atomid);
-}
-
-/*!
- * \param[in] a Index group to check.
- * \param[in] b Index group to check.
- * \returns TRUE if \p a and \p b are equal, FALSE otherwise.
- */
-gmx_bool
-gmx_ana_index_equals(gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int i;
-
- if (a->isize != b->isize)
- {
- return FALSE;
- }
- for (i = 0; i < a->isize; ++i)
- {
- if (a->index[i] != b->index[i])
- {
- return FALSE;
- }
- }
- return TRUE;
-}
-
-/*!
- * \param[in] a Index group to check against.
- * \param[in] b Index group to check.
- * \returns TRUE if \p b is contained in \p a,
- * FALSE otherwise.
- *
- * If the elements are not in the same order in both groups, the function
- * fails. However, the groups do not need to be sorted.
- */
-gmx_bool
-gmx_ana_index_contains(gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int i, j;
-
- for (i = j = 0; j < b->isize; ++i, ++j) {
- while (i < a->isize && a->index[i] != b->index[j])
- {
- ++i;
- }
- if (i == a->isize)
- {
- return FALSE;
- }
- }
- return TRUE;
-}
-
-/*!
- * \param[out] dest Output index group (the intersection of \p a and \p b).
- * \param[in] a First index group.
- * \param[in] b Second index group.
- *
- * \p dest can be the same as \p a or \p b.
- */
-void
-gmx_ana_index_intersection(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int i, j, k;
-
- for (i = j = k = 0; i < a->isize && j < b->isize; ++i) {
- while (j < b->isize && b->index[j] < a->index[i])
- {
- ++j;
- }
- if (j < b->isize && b->index[j] == a->index[i])
- {
- dest->index[k++] = b->index[j++];
- }
- }
- dest->isize = k;
-}
-
-/*!
- * \param[out] dest Output index group (the difference \p a - \p b).
- * \param[in] a First index group.
- * \param[in] b Second index group.
- *
- * \p dest can equal \p a, but not \p b.
- */
-void
-gmx_ana_index_difference(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int i, j, k;
-
- for (i = j = k = 0; i < a->isize; ++i)
- {
- while (j < b->isize && b->index[j] < a->index[i])
- {
- ++j;
- }
- if (j == b->isize || b->index[j] != a->index[i])
- {
- dest->index[k++] = a->index[i];
- }
- }
- dest->isize = k;
-}
-
-/*!
- * \param[in] a First index group.
- * \param[in] b Second index group.
- * \returns Size of the difference \p a - \p b.
- */
-int
-gmx_ana_index_difference_size(gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int i, j, k;
-
- for (i = j = k = 0; i < a->isize; ++i)
- {
- while (j < b->isize && b->index[j] < a->index[i])
- {
- ++j;
- }
- if (j == b->isize || b->index[j] != a->index[i])
- {
- ++k;
- }
- }
- return k;
-}
-
-/*!
- * \param[out] dest1 Output group 1 (will equal \p g).
- * \param[out] dest2 Output group 2 (will equal \p src - \p g).
- * \param[in] src Group to be partitioned.
- * \param[in] g One partition.
- *
- * \pre \p g is a subset of \p src and both sets are sorted
- * \pre \p dest1 has allocated storage to store \p src
- * \post \p dest1 == \p g
- * \post \p dest2 == \p src - \p g
- *
- * No storage should be allocated for \p dest2; after the call,
- * \p dest2->index points to the memory allocated for \p dest1
- * (to a part that is not used by \p dest1).
- *
- * The calculation can be performed in-place by setting \p dest1 equal to
- * \p src.
- */
-void
-gmx_ana_index_partition(gmx_ana_index_t *dest1, gmx_ana_index_t *dest2,
- gmx_ana_index_t *src, gmx_ana_index_t *g)
-
-{
- int i, j, k;
-
- dest2->index = dest1->index + g->isize;
- dest2->isize = src->isize - g->isize;
- for (i = g->isize-1, j = src->isize-1, k = dest2->isize-1; i >= 0; --i, --j)
- {
- while (j >= 0 && src->index[j] != g->index[i])
- {
- dest2->index[k--] = src->index[j--];
- }
- }
- while (j >= 0)
- {
- dest2->index[k--] = src->index[j--];
- }
- gmx_ana_index_copy(dest1, g, FALSE);
-}
-
-/*!
- * \param[out] dest Output index group (the union of \p a and \p b).
- * \param[in] a First index group.
- * \param[in] b Second index group.
- *
- * \p a and \p b can have common items.
- * \p dest can equal \p a or \p b.
- *
- * \see gmx_ana_index_merge()
- */
-void
-gmx_ana_index_union(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int dsize;
- int i, j, k;
-
- dsize = gmx_ana_index_difference_size(b, a);
- i = a->isize - 1;
- j = b->isize - 1;
- dest->isize = a->isize + dsize;
- for (k = dest->isize - 1; k >= 0; k--)
- {
- if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
- {
- dest->index[k] = b->index[j--];
- }
- else
- {
- if (j >= 0 && a->index[i] == b->index[j])
- {
- --j;
- }
- dest->index[k] = a->index[i--];
- }
- }
-}
-
-/*!
- * \param[out] dest Output index group (the union of \p a and \p b).
- * \param[in] a First index group.
- * \param[in] b Second index group.
- *
- * \p a and \p b should not have common items.
- * \p dest can equal \p a or \p b.
- *
- * \see gmx_ana_index_union()
- */
-void
-gmx_ana_index_merge(gmx_ana_index_t *dest,
- gmx_ana_index_t *a, gmx_ana_index_t *b)
-{
- int i, j, k;
-
- i = a->isize - 1;
- j = b->isize - 1;
- dest->isize = a->isize + b->isize;
- for (k = dest->isize - 1; k >= 0; k--)
- {
- if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
- {
- dest->index[k] = b->index[j--];
- }
- else
- {
- dest->index[k] = a->index[i--];
- }
- }
-}
-
-/********************************************************************
- * gmx_ana_indexmap_t and related things
- ********************************************************************/
-
-/*!
- * \param[in,out] t Output block.
- * \param[in] top Topology structure
- * (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL
- * otherwise).
- * \param[in] g Index group
- * (can be NULL if \p type is \ref INDEX_UNKNOWN).
- * \param[in] type Type of partitioning to make.
- * \param[in] bComplete
- * If TRUE, the index group is expanded to include any residue/molecule
- * (depending on \p type) that is partially contained in the group.
- * If \p type is not INDEX_RES or INDEX_MOL, this has no effect.
- *
- * \p m should have been initialized somehow (calloc() is enough) unless
- * \p type is INDEX_UNKNOWN.
- * \p g should be sorted.
- */
-void
-gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g,
- e_index_t type, gmx_bool bComplete)
-{
- int i, j, ai;
- int id, cur;
-
- if (type == INDEX_UNKNOWN)
- {
- t->nr = 1;
- snew(t->index, 2);
- t->nalloc_index = 2;
- t->index[0] = 0;
- t->index[1] = 0;
- t->nra = 0;
- t->a = NULL;
- t->nalloc_a = 0;
- return;
- }
-
- /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it
- * off otherwise. */
- if (type != INDEX_RES && type != INDEX_MOL)
- {
- bComplete = FALSE;
- }
- /* Allocate memory for the atom array and fill it unless we are using
- * completion. */
- if (bComplete)
- {
- t->nra = 0;
- /* We may allocate some extra memory here because we don't know in
- * advance how much will be needed. */
- if (t->nalloc_a < top->atoms.nr)
- {
- srenew(t->a, top->atoms.nr);
- t->nalloc_a = top->atoms.nr;
- }
- }
- else
- {
- t->nra = g->isize;
- if (t->nalloc_a < g->isize)
- {
- srenew(t->a, g->isize);
- t->nalloc_a = g->isize;
- }
- memcpy(t->a, g->index, g->isize*sizeof(*(t->a)));
- }
-
- /* Allocate memory for the block index. We don't know in advance
- * how much will be needed, so we allocate some extra and free it in the
- * end. */
- if (t->nalloc_index < g->isize + 1)
- {
- srenew(t->index, g->isize + 1);
- t->nalloc_index = g->isize + 1;
- }
- /* Clear counters */
- t->nr = 0;
- j = 0; /* j is used by residue completion for the first atom not stored */
- id = cur = -1;
- for (i = 0; i < g->isize; ++i)
- {
- ai = g->index[i];
- /* Find the ID number of the atom/residue/molecule corresponding to
- * atom ai. */
- switch (type)
- {
- case INDEX_ATOM:
- id = ai;
- break;
- case INDEX_RES:
- id = top->atoms.atom[ai].resind;
- break;
- case INDEX_MOL:
- while (ai >= top->mols.index[id+1])
- {
- id++;
- }
- break;
- case INDEX_UNKNOWN: /* Should not occur */
- case INDEX_ALL:
- id = 0;
- break;
- }
- /* If this is the first atom in a new block, initialize the block. */
- if (id != cur)
- {
- if (bComplete)
- {
- /* For completion, we first set the start of the block. */
- t->index[t->nr++] = t->nra;
- /* And then we find all the atoms that should be included. */
- switch (type)
- {
- case INDEX_RES:
- while (top->atoms.atom[j].resind != id)
- {
- ++j;
- }
- while (j < top->atoms.nr && top->atoms.atom[j].resind == id)
- {
- t->a[t->nra++] = j;
- ++j;
- }
- break;
-
- case INDEX_MOL:
- for (j = top->mols.index[id]; j < top->mols.index[id+1]; ++j)
- {
- t->a[t->nra++] = j;
- }
- break;
-
- default: /* Should not be reached */
- gmx_bug("internal error");
- break;
- }
- }
- else
- {
- /* If not using completion, simply store the start of the block. */
- t->index[t->nr++] = i;
- }
- cur = id;
- }
- }
- /* Set the end of the last block */
- t->index[t->nr] = t->nra;
- /* Free any unnecessary memory */
- srenew(t->index, t->nr+1);
- t->nalloc_index = t->nr+1;
- if (bComplete)
- {
- srenew(t->a, t->nra);
- t->nalloc_a = t->nra;
- }
-}
-
-/*!
- * \param[in] g Index group to check.
- * \param[in] b Block data to check against.
- * \returns TRUE if \p g consists of one or more complete blocks from \p b,
- * FALSE otherwise.
- *
- * The atoms in \p g are assumed to be sorted.
- */
-gmx_bool
-gmx_ana_index_has_full_blocks(gmx_ana_index_t *g, t_block *b)
-{
- int i, j, bi;
-
- i = bi = 0;
- /* Each round in the loop matches one block */
- while (i < g->isize)
- {
- /* Find the block that begins with the first unmatched atom */
- while (bi < b->nr && b->index[bi] != g->index[i])
- {
- ++bi;
- }
- /* If not found, or if too large, return */
- if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
- {
- return FALSE;
- }
- /* Check that the block matches the index */
- for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
- {
- if (g->index[i] != j)
- {
- return FALSE;
- }
- }
- /* Move the search to the next block */
- ++bi;
- }
- return TRUE;
-}
-
-/*!
- * \param[in] g Index group to check.
- * \param[in] b Block data to check against.
- * \returns TRUE if \p g consists of one or more complete blocks from \p b,
- * FALSE otherwise.
- *
- * The atoms in \p g and \p b->a are assumed to be in the same order.
- */
-gmx_bool
-gmx_ana_index_has_full_ablocks(gmx_ana_index_t *g, t_blocka *b)
-{
- int i, j, bi;
-
- i = bi = 0;
- /* Each round in the loop matches one block */
- while (i < g->isize)
- {
- /* Find the block that begins with the first unmatched atom */
- while (bi < b->nr && b->a[b->index[bi]] != g->index[i])
- {
- ++bi;
- }
- /* If not found, or if too large, return */
- if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
- {
- return FALSE;
- }
- /* Check that the block matches the index */
- for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
- {
- if (b->a[j] != g->index[i])
- {
- return FALSE;
- }
- }
- /* Move the search to the next block */
- ++bi;
- }
- return TRUE;
-}
-
-/*!
- * \param[in] g Index group to check.
- * \param[in] type Block data to check against.
- * \param[in] top Topology data.
- * \returns TRUE if \p g consists of one or more complete elements of type
- * \p type, FALSE otherwise.
- *
- * If \p type is \ref INDEX_ATOM, the return value is always TRUE.
- * If \p type is \ref INDEX_UNKNOWN or \ref INDEX_ALL, the return value is
- * always FALSE.
- */
-gmx_bool
-gmx_ana_index_has_complete_elems(gmx_ana_index_t *g, e_index_t type,
- t_topology *top)
-{
- switch (type)
- {
- case INDEX_UNKNOWN:
- case INDEX_ALL:
- return FALSE;
-
- case INDEX_ATOM:
- return TRUE;
-
- case INDEX_RES:
- {
- int i, ai;
- int id, prev;
-
- prev = -1;
- for (i = 0; i < g->isize; ++i)
- {
- ai = g->index[i];
- id = top->atoms.atom[ai].resind;
- if (id != prev)
- {
- if (ai > 0 && top->atoms.atom[ai-1].resind == id)
- {
- return FALSE;
- }
- if (i > 0 && g->index[i-1] < top->atoms.nr - 1
- && top->atoms.atom[g->index[i-1]+1].resind == prev)
- {
- return FALSE;
- }
- }
- prev = id;
- }
- if (g->index[i-1] < top->atoms.nr - 1
- && top->atoms.atom[g->index[i-1]+1].resind == prev)
- {
- return FALSE;
- }
- break;
- }
-
- case INDEX_MOL:
- return gmx_ana_index_has_full_blocks(g, &top->mols);
- }
- return TRUE;
-}
-
-/*!
- * \param[out] m Output structure.
- *
- * Any contents of \p m are discarded without freeing.
- */
-void
-gmx_ana_indexmap_clear(gmx_ana_indexmap_t *m)
-{
- m->type = INDEX_UNKNOWN;
- m->nr = 0;
- m->refid = NULL;
- m->mapid = NULL;
- m->mapb.nr = 0;
- m->mapb.index = NULL;
- m->mapb.nalloc_index = 0;
- m->orgid = NULL;
- m->b.nr = 0;
- m->b.index = NULL;
- m->b.nra = 0;
- m->b.a = NULL;
- m->b.nalloc_index = 0;
- m->b.nalloc_a = 0;
- m->bStatic = TRUE;
- m->bMapStatic = TRUE;
-}
-
-/*!
- * \param[in,out] m Mapping structure.
- * \param[in] nr Maximum number of blocks to reserve space for.
- * \param[in] isize Maximum number of atoms to reserve space for.
- */
-void
-gmx_ana_indexmap_reserve(gmx_ana_indexmap_t *m, int nr, int isize)
-{
- if (m->mapb.nalloc_index < nr + 1)
- {
- srenew(m->refid, nr);
- srenew(m->mapid, nr);
- srenew(m->orgid, nr);
- srenew(m->mapb.index, nr + 1);
- srenew(m->b.index, nr + 1);
- m->mapb.nalloc_index = nr + 1;
- m->b.nalloc_index = nr + 1;
- }
- if (m->b.nalloc_a < isize)
- {
- srenew(m->b.a, isize);
- m->b.nalloc_a = isize;
- }
-}
-
-/*!
- * \param[in,out] m Mapping structure to initialize.
- * \param[in] g Index group to map
- * (can be NULL if \p type is \ref INDEX_UNKNOWN).
- * \param[in] top Topology structure
- * (can be NULL if \p type is not \ref INDEX_RES or \ref INDEX_MOL).
- * \param[in] type Type of mapping to construct.
- *
- * Initializes a new index group mapping.
- * The index group provided to gmx_ana_indexmap_update() should always be a
- * subset of the \p g given here.
- *
- * \p m should have been initialized somehow (calloc() is enough).
- */
-void
-gmx_ana_indexmap_init(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
- t_topology *top, e_index_t type)
-{
- int i, ii, mi;
-
- m->type = type;
- gmx_ana_index_make_block(&m->b, top, g, type, FALSE);
- gmx_ana_indexmap_reserve(m, m->b.nr, m->b.nra);
- m->nr = m->b.nr;
- for (i = mi = 0; i < m->nr; ++i)
- {
- ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
- switch (type)
- {
- case INDEX_ATOM:
- m->orgid[i] = ii;
- break;
- case INDEX_RES:
- m->orgid[i] = top->atoms.atom[ii].resind;
- break;
- case INDEX_MOL:
- while (top->mols.index[mi+1] <= ii)
- {
- ++mi;
- }
- m->orgid[i] = mi;
- break;
- case INDEX_ALL:
- case INDEX_UNKNOWN:
- m->orgid[i] = 0;
- break;
- }
- }
- for (i = 0; i < m->nr; ++i)
- {
- m->refid[i] = i;
- m->mapid[i] = m->orgid[i];
- }
- m->mapb.nr = m->nr;
- memcpy(m->mapb.index, m->b.index, (m->nr+1)*sizeof(*(m->mapb.index)));
- m->bStatic = TRUE;
- m->bMapStatic = TRUE;
-}
-
-/*!
- * \param[in,out] m Mapping structure to initialize.
- * \param[in] b Block information to use for data.
- *
- * Frees some memory that is not necessary for static index group mappings.
- * Internal pointers are set to point to data in \p b; it is the responsibility
- * of the caller to ensure that the block information matches the contents of
- * the mapping.
- * After this function has been called, the index group provided to
- * gmx_ana_indexmap_update() should always be the same as \p g given here.
- *
- * This function breaks modularity of the index group mapping interface in an
- * ugly way, but allows reducing memory usage of static selections by a
- * significant amount.
- */
-void
-gmx_ana_indexmap_set_static(gmx_ana_indexmap_t *m, t_blocka *b)
-{
- sfree(m->mapid);
- m->mapid = m->orgid;
- sfree(m->b.index);
- m->b.nalloc_index = 0;
- m->b.index = b->index;
- sfree(m->mapb.index);
- m->mapb.nalloc_index = 0;
- m->mapb.index = m->b.index;
- sfree(m->b.a);
- m->b.nalloc_a = 0;
- m->b.a = b->a;
-}
-
-/*!
- * \param[in,out] dest Destination data structure.
- * \param[in] src Source mapping.
- * \param[in] bFirst If TRUE, memory is allocated for \p dest and a full
- * copy is made; otherwise, only variable parts are copied, and no memory
- * is allocated.
- *
- * \p dest should have been initialized somehow (calloc() is enough).
- */
-void
-gmx_ana_indexmap_copy(gmx_ana_indexmap_t *dest, gmx_ana_indexmap_t *src, gmx_bool bFirst)
-{
- if (bFirst)
- {
- gmx_ana_indexmap_reserve(dest, src->b.nr, src->b.nra);
- dest->type = src->type;
- dest->b.nr = src->b.nr;
- dest->b.nra = src->b.nra;
- memcpy(dest->orgid, src->orgid, dest->b.nr*sizeof(*dest->orgid));
- memcpy(dest->b.index, src->b.index, (dest->b.nr+1)*sizeof(*dest->b.index));
- memcpy(dest->b.a, src->b.a, dest->b.nra*sizeof(*dest->b.a));
- }
- dest->nr = src->nr;
- dest->mapb.nr = src->mapb.nr;
- memcpy(dest->refid, src->refid, dest->nr*sizeof(*dest->refid));
- memcpy(dest->mapid, src->mapid, dest->nr*sizeof(*dest->mapid));
- memcpy(dest->mapb.index, src->mapb.index,(dest->mapb.nr+1)*sizeof(*dest->mapb.index));
- dest->bStatic = src->bStatic;
- dest->bMapStatic = src->bMapStatic;
-}
-
-/*!
- * \param[in,out] m Mapping structure.
- * \param[in] g Current index group.
- * \param[in] bMaskOnly TRUE if the unused blocks should be masked with
- * -1 instead of removing them.
- *
- * Updates the index group mapping with the new index group \p g.
- *
- * \see gmx_ana_indexmap_t
- */
-void
-gmx_ana_indexmap_update(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
- gmx_bool bMaskOnly)
-{
- int i, j, bi, bj;
- gmx_bool bStatic;
-
- /* Process the simple cases first */
- if (m->type == INDEX_UNKNOWN && m->b.nra == 0)
- {
- return;
- }
- if (m->type == INDEX_ALL)
- {
- if (m->b.nr > 0)
- {
- m->mapb.index[1] = g->isize;
- }
- return;
- }
- /* Reset the reference IDs and mapping if necessary */
- bStatic = (g->isize == m->b.nra && m->nr == m->b.nr);
- if (bStatic || bMaskOnly)
- {
- if (!m->bStatic)
- {
- for (bj = 0; bj < m->b.nr; ++bj)
- {
- m->refid[bj] = bj;
- }
- }
- if (!m->bMapStatic)
- {
- for (bj = 0; bj < m->b.nr; ++bj)
- {
- m->mapid[bj] = m->orgid[bj];
- }
- for (bj = 0; bj <= m->b.nr; ++bj)
- {
- m->mapb.index[bj] = m->b.index[bj];
- }
- m->bMapStatic = TRUE;
- }
- }
- /* Exit immediately if the group is static */
- if (bStatic)
- {
- m->bStatic = TRUE;
- return;
- }
-
- if (bMaskOnly)
- {
- m->nr = m->b.nr;
- for (i = j = bj = 0; i < g->isize; ++i, ++j)
- {
- /* Find the next atom in the block */
- while (m->b.a[j] != g->index[i])
- {
- ++j;
- }
- /* Mark blocks that did not contain any atoms */
- while (bj < m->b.nr && m->b.index[bj+1] <= j)
- {
- m->refid[bj++] = -1;
- }
- /* Advance the block index if we have reached the next block */
- if (m->b.index[bj] <= j)
- {
- ++bj;
- }
- }
- /* Mark the last blocks as not accessible */
- while (bj < m->b.nr)
- {
- m->refid[bj++] = -1;
- }
- }
- else
- {
- for (i = j = bi = 0, bj = -1; i < g->isize; ++i)
- {
- /* Find the next atom in the block */
- while (m->b.a[j] != g->index[i])
- {
- ++j;
- }
- /* If we have reached a new block, add it */
- if (m->b.index[bj+1] <= j)
- {
- /* Skip any blocks in between */
- while (bj < m->b.nr && m->b.index[bj+1] <= j)
- {
- ++bj;
- }
- m->refid[bi] = bj;
- m->mapid[bi] = m->orgid[bj];
- m->mapb.index[bi] = i;
- bi++;
- }
- }
- /* Update the number of blocks */
- m->mapb.index[bi] = g->isize;
- m->nr = bi;
- m->bMapStatic = FALSE;
- }
- m->mapb.nr = m->nr;
- m->bStatic = FALSE;
-}
-
-/*!
- * \param[in,out] m Mapping structure to free.
- *
- * All the memory allocated for the mapping structure is freed, and
- * the pointers set to NULL.
- * The pointer \p m is not freed.
- */
-void
-gmx_ana_indexmap_deinit(gmx_ana_indexmap_t *m)
-{
- sfree(m->refid);
- if (m->mapid != m->orgid)
- {
- sfree(m->mapid);
- }
- if (m->mapb.nalloc_index > 0)
- {
- sfree(m->mapb.index);
- }
- sfree(m->orgid);
- if (m->b.nalloc_index > 0)
- {
- sfree(m->b.index);
- }
- if (m->b.nalloc_a > 0)
- {
- sfree(m->b.a);
- }
- gmx_ana_indexmap_clear(m);
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \page nbsearch Neighborhood search routines
- *
- * Functions to find particles within a neighborhood of a set of particles
- * are defined in nbsearch.h.
- * The usage is simple: a data structure is allocated with
- * gmx_ana_nbsearch_create(), and the box shape and reference positions for a
- * frame are set using gmx_ana_nbsearch_init() or gmx_ana_nbsearch_pos_init().
- * Searches can then be performed with gmx_ana_nbsearch_is_within() and
- * gmx_ana_nbsearch_mindist(), or with versions that take the \c gmx_ana_pos_t
- * data structure.
- * When the data structure is no longer required, it can be freed with
- * gmx_ana_nbsearch_free().
- *
- * \internal
- *
- * \todo
- * The grid implementation could still be optimized in several different ways:
- * - Triclinic grid cells are not the most efficient shape, but make PBC
- * handling easier.
- * - Precalculating the required PBC shift for a pair of cells outside the
- * inner loop. After this is done, it should be quite straightforward to
- * move to rectangular cells.
- * - Pruning grid cells from the search list if they are completely outside
- * the sphere that is being considered.
- * - A better heuristic could be added for falling back to simple loops for a
- * small number of reference particles.
- * - A better heuristic for selecting the grid size.
- * - A multi-level grid implementation could be used to be able to use small
- * grids for short cutoffs with very inhomogeneous particle distributions
- * without a memory cost.
- */
-/*! \internal \file
- * \brief Implementation of functions in nbsearch.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <math.h>
-
-#include <smalloc.h>
-#include <typedefs.h>
-#include <pbc.h>
-#include <vec.h>
-
-#include <nbsearch.h>
-#include <position.h>
-
-/*! \internal \brief
- * Data structure for neighborhood searches.
- */
-struct gmx_ana_nbsearch_t
-{
- /** The cutoff. */
- real cutoff;
- /** The cutoff squared. */
- real cutoff2;
- /** Maximum number of reference points. */
- int maxnref;
-
- /** Number of reference points for the current frame. */
- int nref;
- /** Reference point positions. */
- rvec *xref;
- /** Reference position ids (NULL if not available). */
- int *refid;
- /** PBC data. */
- t_pbc *pbc;
-
- /** Number of excluded reference positions for current test particle. */
- int nexcl;
- /** Exclusions for current test particle. */
- int *excl;
-
- /** Whether to try grid searching. */
- gmx_bool bTryGrid;
- /** Whether grid searching is actually used for the current positions. */
- gmx_bool bGrid;
- /** Array allocated for storing in-unit-cell reference positions. */
- rvec *xref_alloc;
- /** FALSE if the box is rectangular. */
- gmx_bool bTric;
- /** Box vectors of a single grid cell. */
- matrix cellbox;
- /** The reciprocal cell vectors as columns; the inverse of \p cellbox. */
- matrix recipcell;
- /** Number of cells along each dimension. */
- ivec ncelldim;
- /** Total number of cells. */
- int ncells;
- /** Number of reference positions in each cell. */
- int *ncatoms;
- /** List of reference positions in each cell. */
- atom_id **catom;
- /** Allocation counts for each \p catom[i]. */
- int *catom_nalloc;
- /** Allocation count for the per-cell arrays. */
- int cells_nalloc;
- /** Number of neighboring cells to consider. */
- int ngridnb;
- /** Offsets of the neighboring cells to consider. */
- ivec *gnboffs;
- /** Allocation count for \p gnboffs. */
- int gnboffs_nalloc;
-
- /** Stores test position during a pair loop. */
- rvec xtest;
- /** Stores the previous returned position during a pair loop. */
- int previ;
- /** Stores the current exclusion index during loops. */
- int exclind;
- /** Stores the test particle cell index during loops. */
- ivec testcell;
- /** Stores the current cell neighbor index during pair loops. */
- int prevnbi;
- /** Stores the index within the current cell during pair loops. */
- int prevcai;
-};
-
-/*!
- * \param[out] data Neighborhood search data structure pointer to initialize.
- * \param[in] cutoff Cutoff distance for the search
- * (<=0 stands for no cutoff).
- * \param[in] maxn Maximum number of reference particles.
- * \returns 0 on success.
- */
-int
-gmx_ana_nbsearch_create(gmx_ana_nbsearch_t **data, real cutoff, int maxn)
-{
- gmx_ana_nbsearch_t *d;
-
- snew(d, 1);
- d->bTryGrid = TRUE;
- if (cutoff <= 0)
- {
- cutoff = HUGE_VAL;
- d->bTryGrid = FALSE;
- }
- d->cutoff = cutoff;
- d->cutoff2 = sqr(cutoff);
- d->maxnref = maxn;
-
- d->xref = NULL;
- d->nexcl = 0;
- d->exclind = 0;
-
- d->xref_alloc = NULL;
- d->ncells = 0;
- d->ncatoms = NULL;
- d->catom = NULL;
- d->catom_nalloc = 0;
- d->cells_nalloc = 0;
-
- d->ngridnb = 0;
- d->gnboffs = NULL;
- d->gnboffs_nalloc = 0;
-
- *data = d;
- return 0;
-}
-
-/*!
- * \param d Data structure to free.
- *
- * After the call, the pointer \p d is no longer valid.
- */
-void
-gmx_ana_nbsearch_free(gmx_ana_nbsearch_t *d)
-{
- sfree(d->xref_alloc);
- sfree(d->ncatoms);
- if (d->catom)
- {
- int ci;
-
- for (ci = 0; ci < d->ncells; ++ci)
- {
- sfree(d->catom[ci]);
- }
- sfree(d->catom);
- }
- sfree(d->catom_nalloc);
- sfree(d->gnboffs);
- sfree(d);
-}
-
-/*! \brief
- * Calculates offsets to neighboring grid cells that should be considered.
- *
- * \param[in,out] d Grid information.
- * \param[in] pbc Information about the box.
- */
-static void
-grid_init_cell_nblist(gmx_ana_nbsearch_t *d, t_pbc *pbc)
-{
- int maxx, maxy, maxz;
- int x, y, z, i;
- real rvnorm;
-
- /* Find the extent of the sphere in triclinic coordinates */
- maxz = (int)(d->cutoff * d->recipcell[ZZ][ZZ]) + 1;
- rvnorm = sqrt(sqr(d->recipcell[YY][YY]) + sqr(d->recipcell[ZZ][YY]));
- maxy = (int)(d->cutoff * rvnorm) + 1;
- rvnorm = sqrt(sqr(d->recipcell[XX][XX]) + sqr(d->recipcell[YY][XX])
- + sqr(d->recipcell[ZZ][XX]));
- maxx = (int)(d->cutoff * rvnorm) + 1;
-
- /* Calculate the number of cells and reallocate if necessary */
- d->ngridnb = (2 * maxx + 1) * (2 * maxy + 1) * (2 * maxz + 1);
- if (d->gnboffs_nalloc < d->ngridnb)
- {
- d->gnboffs_nalloc = d->ngridnb;
- srenew(d->gnboffs, d->gnboffs_nalloc);
- }
-
- /* Store the whole cube */
- /* TODO: Prune off corners that are not needed */
- i = 0;
- for (x = -maxx; x <= maxx; ++x)
- {
- for (y = -maxy; y <= maxy; ++y)
- {
- for (z = -maxz; z <= maxz; ++z)
- {
- d->gnboffs[i][XX] = x;
- d->gnboffs[i][YY] = y;
- d->gnboffs[i][ZZ] = z;
- ++i;
- }
- }
- }
-}
-
-/*! \brief
- * Determines a suitable grid size.
- *
- * \param[in,out] d Grid information.
- * \param[in] pbc Information about the box.
- * \returns FALSE if grid search is not suitable.
- */
-static gmx_bool
-grid_setup_cells(gmx_ana_nbsearch_t *d, t_pbc *pbc)
-{
- real targetsize;
- int dd;
-
-#ifdef HAVE_CBRT
- targetsize = cbrt(pbc->box[XX][XX] * pbc->box[YY][YY] * pbc->box[ZZ][ZZ]
- * 10 / d->nref);
-#else
- targetsize = pow(pbc->box[XX][XX] * pbc->box[YY][YY] * pbc->box[ZZ][ZZ]
- * 10 / d->nref, 1./3.);
-#endif
-
- d->ncells = 1;
- for (dd = 0; dd < DIM; ++dd)
- {
- d->ncelldim[dd] = (int)(pbc->box[dd][dd] / targetsize);
- d->ncells *= d->ncelldim[dd];
- if (d->ncelldim[dd] < 3)
- {
- return FALSE;
- }
- }
- /* Reallocate if necessary */
- if (d->cells_nalloc < d->ncells)
- {
- int i;
-
- srenew(d->ncatoms, d->ncells);
- srenew(d->catom, d->ncells);
- srenew(d->catom_nalloc, d->ncells);
- for (i = d->cells_nalloc; i < d->ncells; ++i)
- {
- d->catom[i] = NULL;
- d->catom_nalloc[i] = 0;
- }
- d->cells_nalloc = d->ncells;
- }
- return TRUE;
-}
-
-/*! \brief
- * Sets ua a search grid for a given box.
- *
- * \param[in,out] d Grid information.
- * \param[in] pbc Information about the box.
- * \returns FALSE if grid search is not suitable.
- */
-static gmx_bool
-grid_set_box(gmx_ana_nbsearch_t *d, t_pbc *pbc)
-{
- int dd;
-
- /* TODO: This check could be improved. */
- if (0.5*pbc->max_cutoff2 < d->cutoff2)
- {
- return FALSE;
- }
-
- if (!grid_setup_cells(d, pbc))
- {
- return FALSE;
- }
-
- d->bTric = TRICLINIC(pbc->box);
- if (d->bTric)
- {
- for (dd = 0; dd < DIM; ++dd)
- {
- svmul(1.0 / d->ncelldim[dd], pbc->box[dd], d->cellbox[dd]);
- }
- m_inv_ur0(d->cellbox, d->recipcell);
- }
- else
- {
- for (dd = 0; dd < DIM; ++dd)
- {
- d->cellbox[dd][dd] = pbc->box[dd][dd] / d->ncelldim[dd];
- d->recipcell[dd][dd] = 1 / d->cellbox[dd][dd];
- }
- }
- grid_init_cell_nblist(d, pbc);
- return TRUE;
-}
-
-/*! \brief
- * Maps a point into a grid cell.
- *
- * \param[in] d Grid information.
- * \param[in] x Point to map.
- * \param[out] cell Indices of the grid cell in which \p x lies.
- *
- * \p x should be in the triclinic unit cell.
- */
-static void
-grid_map_onto(gmx_ana_nbsearch_t *d, const rvec x, ivec cell)
-{
- int dd;
-
- if (d->bTric)
- {
- rvec tx;
-
- tmvmul_ur0(d->recipcell, x, tx);
- for (dd = 0; dd < DIM; ++dd)
- {
- cell[dd] = (int)tx[dd];
- }
- }
- else
- {
- for (dd = 0; dd < DIM; ++dd)
- {
- cell[dd] = (int)(x[dd] * d->recipcell[dd][dd]);
- }
- }
-}
-
-/*! \brief
- * Calculates linear index of a grid cell.
- *
- * \param[in] d Grid information.
- * \param[in] cell Cell indices.
- * \returns Linear index of \p cell.
- */
-static int
-grid_index(gmx_ana_nbsearch_t *d, const ivec cell)
-{
- return cell[XX] + cell[YY] * d->ncelldim[XX]
- + cell[ZZ] * d->ncelldim[XX] * d->ncelldim[YY];
-}
-
-/*! \brief
- * Clears all grid cells.
- *
- * \param[in,out] d Grid information.
- */
-static void
-grid_clear_cells(gmx_ana_nbsearch_t *d)
-{
- int ci;
-
- for (ci = 0; ci < d->ncells; ++ci)
- {
- d->ncatoms[ci] = 0;
- }
-}
-
-/*! \brief
- * Adds an index into a grid cell.
- *
- * \param[in,out] d Grid information.
- * \param[in] cell Cell into which \p i should be added.
- * \param[in] i Index to add.
- */
-static void
-grid_add_to_cell(gmx_ana_nbsearch_t *d, const ivec cell, int i)
-{
- int ci = grid_index(d, cell);
-
- if (d->ncatoms[ci] == d->catom_nalloc[ci])
- {
- d->catom_nalloc[ci] += 10;
- srenew(d->catom[ci], d->catom_nalloc[ci]);
- }
- d->catom[ci][d->ncatoms[ci]++] = i;
-}
-
-/*!
- * \param[in,out] d Neighborhood search data structure.
- * \param[in] pbc PBC information for the frame.
- * \param[in] n Number of reference positions for the frame.
- * \param[in] x \p n reference positions for the frame.
- * \returns 0 on success.
- *
- * Initializes the data structure \p d such that it can be used to search
- * for the neighbors of \p x.
- */
-int
-gmx_ana_nbsearch_init(gmx_ana_nbsearch_t *d, t_pbc *pbc, int n, rvec x[])
-{
- d->pbc = pbc;
- d->nref = n;
- if (!pbc)
- {
- d->bGrid = FALSE;
- }
- else if (d->bTryGrid)
- {
- d->bGrid = grid_set_box(d, pbc);
- }
- if (d->bGrid)
- {
- int i;
-
- if (!d->xref_alloc)
- {
- snew(d->xref_alloc, d->maxnref);
- }
- d->xref = d->xref_alloc;
- grid_clear_cells(d);
-
- for (i = 0; i < n; ++i)
- {
- copy_rvec(x[i], d->xref[i]);
- }
- put_atoms_in_triclinic_unitcell(ecenterTRIC, pbc->box, n, d->xref);
- for (i = 0; i < n; ++i)
- {
- ivec refcell;
-
- grid_map_onto(d, d->xref[i], refcell);
- grid_add_to_cell(d, refcell, i);
- }
- }
- else
- {
- d->xref = x;
- }
- d->refid = NULL;
- return 0;
-}
-
-/*!
- * \param[in,out] d Neighborhood search data structure.
- * \param[in] pbc PBC information for the frame.
- * \param[in] p Reference positions for the frame.
- * \returns 0 on success.
- *
- * A convenience wrapper for gmx_ana_nbsearch_init().
- */
-int
-gmx_ana_nbsearch_pos_init(gmx_ana_nbsearch_t *d, t_pbc *pbc, gmx_ana_pos_t *p)
-{
- int rc;
-
- rc = gmx_ana_nbsearch_init(d, pbc, p->nr, p->x);
- d->refid = (p->nr < d->maxnref ? p->m.refid : NULL);
- return rc;
-}
-
-/*!
- * \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.
- * \returns 0 on success.
- *
- * The set exclusions remain in effect until the next call of this function.
- */
-int
-gmx_ana_nbsearch_set_excl(gmx_ana_nbsearch_t *d, int nexcl, int excl[])
-{
-
- d->nexcl = nexcl;
- d->excl = excl;
- return 0;
-}
-
-/*! \brief
- * Helper function to check whether a reference point should be excluded.
- */
-static gmx_bool
-is_excluded(gmx_ana_nbsearch_t *d, int j)
-{
- if (d->exclind < d->nexcl)
- {
- if (d->refid)
- {
- while (d->exclind < d->nexcl && d->refid[j] > d->excl[d->exclind])
- {
- ++d->exclind;
- }
- if (d->exclind < d->nexcl && d->refid[j] == d->excl[d->exclind])
- {
- ++d->exclind;
- return TRUE;
- }
- }
- else
- {
- while (d->bGrid && d->exclind < d->nexcl && d->excl[d->exclind] < j)
- {
- ++d->exclind;
- }
- if (d->excl[d->exclind] == j)
- {
- ++d->exclind;
- return TRUE;
- }
- }
- }
- return FALSE;
-}
-
-/*! \brief
- * Initializes a grid search to find reference positions neighboring \p x.
- */
-static void
-grid_search_start(gmx_ana_nbsearch_t *d, rvec x)
-{
- copy_rvec(x, d->xtest);
- if (d->bGrid)
- {
- put_atoms_in_triclinic_unitcell(ecenterTRIC, d->pbc->box, 1, &d->xtest);
- grid_map_onto(d, d->xtest, d->testcell);
- d->prevnbi = 0;
- d->prevcai = -1;
- }
- else
- {
- d->previ = -1;
- }
- d->exclind = 0;
-}
-
-/*! \brief
- * Does a grid search.
- */
-static gmx_bool
-grid_search(gmx_ana_nbsearch_t *d,
- gmx_bool (*action)(gmx_ana_nbsearch_t *d, int i, real r2))
-{
- int i;
- rvec dx;
- real r2;
-
- if (d->bGrid)
- {
- int nbi, ci, cai;
-
- nbi = d->prevnbi;
- cai = d->prevcai + 1;
-
- for ( ; nbi < d->ngridnb; ++nbi)
- {
- ivec cell;
-
- ivec_add(d->testcell, d->gnboffs[nbi], cell);
- /* TODO: Support for 2D and screw PBC */
- cell[XX] = (cell[XX] + d->ncelldim[XX]) % d->ncelldim[XX];
- cell[YY] = (cell[YY] + d->ncelldim[YY]) % d->ncelldim[YY];
- cell[ZZ] = (cell[ZZ] + d->ncelldim[ZZ]) % d->ncelldim[ZZ];
- ci = grid_index(d, cell);
- /* TODO: Calculate the required PBC shift outside the inner loop */
- for ( ; cai < d->ncatoms[ci]; ++cai)
- {
- i = d->catom[ci][cai];
- if (is_excluded(d, i))
- {
- continue;
- }
- pbc_dx_aiuc(d->pbc, d->xtest, d->xref[i], dx);
- r2 = norm2(dx);
- if (r2 <= d->cutoff2)
- {
- if (action(d, i, r2))
- {
- d->prevnbi = nbi;
- d->prevcai = cai;
- d->previ = i;
- return TRUE;
- }
- }
- }
- d->exclind = 0;
- cai = 0;
- }
- }
- else
- {
- i = d->previ + 1;
- for ( ; i < d->nref; ++i)
- {
- if (is_excluded(d, i))
- {
- continue;
- }
- if (d->pbc)
- {
- pbc_dx(d->pbc, d->xtest, d->xref[i], dx);
- }
- else
- {
- rvec_sub(d->xtest, d->xref[i], dx);
- }
- r2 = norm2(dx);
- if (r2 <= d->cutoff2)
- {
- if (action(d, i, r2))
- {
- d->previ = i;
- return TRUE;
- }
- }
- }
- }
- return FALSE;
-}
-
-/*! \brief
- * Helper function to use with grid_search() to find the next neighbor.
- *
- * Simply breaks the loop on the first found neighbor.
- */
-static gmx_bool
-within_action(gmx_ana_nbsearch_t *d, int i, real r2)
-{
- return TRUE;
-}
-
-/*! \brief
- * Helper function to use with grid_search() to find the minimum distance.
- */
-static gmx_bool
-mindist_action(gmx_ana_nbsearch_t *d, int i, real r2)
-{
- d->cutoff2 = r2;
- return FALSE;
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[in] x Test position.
- * \returns TRUE if \p x is within the cutoff of any reference position,
- * FALSE otherwise.
- */
-gmx_bool
-gmx_ana_nbsearch_is_within(gmx_ana_nbsearch_t *d, rvec x)
-{
- grid_search_start(d, x);
- return grid_search(d, &within_action);
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[in] p Test positions.
- * \param[in] i Use the i'th position in \p p for testing.
- * \returns TRUE if the test position is within the cutoff of any reference
- * position, FALSE otherwise.
- */
-gmx_bool
-gmx_ana_nbsearch_pos_is_within(gmx_ana_nbsearch_t *d, gmx_ana_pos_t *p, int i)
-{
- return gmx_ana_nbsearch_is_within(d, p->x[i]);
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[in] x Test position.
- * \returns The distance to the nearest reference position, or the cutoff
- * value if there are no reference positions within the cutoff.
- */
-real
-gmx_ana_nbsearch_mindist(gmx_ana_nbsearch_t *d, rvec x)
-{
- real mind;
-
- grid_search_start(d, x);
- grid_search(d, &mindist_action);
- mind = sqrt(d->cutoff2);
- d->cutoff2 = sqr(d->cutoff);
- return mind;
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[in] p Test positions.
- * \param[in] i Use the i'th position in \p p for testing.
- * \returns The distance to the nearest reference position, or the cutoff
- * value if there are no reference positions within the cutoff.
- */
-real
-gmx_ana_nbsearch_pos_mindist(gmx_ana_nbsearch_t *d, gmx_ana_pos_t *p, int i)
-{
- return gmx_ana_nbsearch_mindist(d, p->x[i]);
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[in] n Number of test positions in \p x.
- * \param[in] x Test positions.
- * \param[out] jp Index of the reference position in the first pair.
- * \returns TRUE if there are positions within the cutoff.
- */
-gmx_bool
-gmx_ana_nbsearch_first_within(gmx_ana_nbsearch_t *d, rvec x, int *jp)
-{
- grid_search_start(d, x);
- return gmx_ana_nbsearch_next_within(d, jp);
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[in] p Test positions.
- * \param[in] i Use the i'th position in \p p.
- * \param[out] jp Index of the reference position in the first pair.
- * \returns TRUE if there are positions within the cutoff.
- */
-gmx_bool
-gmx_ana_nbsearch_pos_first_within(gmx_ana_nbsearch_t *d, gmx_ana_pos_t *p,
- int i, int *jp)
-{
- return gmx_ana_nbsearch_first_within(d, p->x[i], jp);
-}
-
-/*!
- * \param[in] d Neighborhood search data structure.
- * \param[out] jp Index of the test position in the next pair.
- * \returns TRUE if there are positions within the cutoff.
- */
-gmx_bool
-gmx_ana_nbsearch_next_within(gmx_ana_nbsearch_t *d, int *jp)
-{
- if (grid_search(d, &within_action))
- {
- *jp = d->previ;
- return TRUE;
- }
- *jp = -1;
- return FALSE;
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal
- * \page poscalcengine Position calculation engine
- *
- * The header file \ref poscalc.h defines an API for calculating positions
- * in an automated way. This is useful mostly in the selection engine, in
- * particular with dynamic selections, because the same COM/COG positions
- * may be needed in several contexts. The API makes it possible to
- * optimize the evaluation such that any heavy calculation is only done once,
- * and the results just copied if needed more than once.
- * The functions also provide a convenient interface for keeping the whole
- * \c gmx_ana_pos_t structure up-to-date.
- *
- * A new collection of position calculations is allocated with
- * gmx_ana_poscalc_coll_create().
- * Calculations within one collection should share the same topology, and
- * they are optimized. Calculations in different collections do not interact.
- * The topology for a collection can be set with
- * gmx_ana_poscalc_coll_set_topology().
- * This needs to be done before calling gmx_ana_poscalc_set_maxindex() for
- * any calculation in the collection, unless that calculation does not
- * require topology information.
- * All memory allocated for a collection and the calculations in it can be
- * freed with gmx_ana_poscalc_coll_free().
- *
- * A new calculation is created with gmx_ana_poscalc_create().
- * If flags need to be adjusted later, gmx_ana_poscalc_set_flags() can be
- * used.
- * After the flags are final, the largest possible index group for which the
- * positions are needed has to be set with gmx_ana_poscalc_set_maxindex().
- * gmx_ana_poscalc_coll_set_topology() should have been called before this
- * function is called.
- * After the above calls, gmx_ana_poscalc_init_pos() can be used to initialize
- * output to a \c gmx_ana_pos_t structure. Several different structures can be
- * initialized for the same calculation; the only requirement is that the
- * structure passed later to gmx_ana_poscalc_update() has been initialized
- * properly.
- * The memory allocated for a calculation can be freed with
- * gmx_ana_poscalc_free().
- *
- * The position evaluation is simple: gmx_ana_poscalc_init_frame() should be
- * called once for each frame, and gmx_ana_poscalc_update() can then be called
- * for each calculation that is needed for that frame.
- *
- * It is also possible to initialize the calculations based on a type provided
- * as a string.
- * The possible strings are returned by gmx_ana_poscalc_create_type_enum(),
- * and the string can be converted to the parameters for
- * gmx_ana_poscalc_create() using gmx_ana_poscalc_type_from_enum().
- * gmx_ana_poscalc_create_enum() is also provided for convenience.
- */
-/*! \internal \file
- * \brief Implementation of functions in poscalc.h.
- *
- * \todo
- * There is probably some room for optimization in the calculation of
- * positions with bases.
- * In particular, the current implementation may do a lot of unnecessary
- * copying.
- * The interface would need to be changed to make it possible to use the
- * same output positions for several calculations.
- *
- * \todo
- * The current algorithm for setting up base calculations could be improved
- * in cases when there are calculations that cannot use a common base but
- * still overlap partially (e.g., with three calculations A, B, and C
- * such that A could use both B and C as a base, but B and C cannot use the
- * same base).
- * Setting up the bases in an optimal manner in every possible situation can
- * be quite difficult unless several bases are allowed for one calculation,
- * but better heuristics could probably be implemented.
- * For best results, the setup should probably be postponed (at least
- * partially) to gmx_ana_poscalc_init_eval().
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <macros.h>
-#include <smalloc.h>
-#include <typedefs.h>
-#include <pbc.h>
-#include <vec.h>
-
-#include <centerofmass.h>
-#include <indexutil.h>
-#include <poscalc.h>
-#include <position.h>
-
-/*! \internal \brief
- * Collection of \c gmx_ana_poscalc_t structures for the same topology.
- *
- * Calculations within the same structure are optimized to eliminate duplicate
- * calculations.
- */
-struct gmx_ana_poscalc_coll_t
-{
- /*! \brief
- * Topology data.
- *
- * Can be NULL if none of the calculations require topology data or if
- * gmx_ana_poscalc_coll_set_topology() has not been called.
- */
- t_topology *top;
- /** Pointer to the first data structure. */
- gmx_ana_poscalc_t *first;
- /** Pointer to the last data structure. */
- gmx_ana_poscalc_t *last;
- /** Whether the collection has been initialized for evaluation. */
- gmx_bool bInit;
-};
-
-/*! \internal \brief
- * Data structure for position calculation.
- */
-struct gmx_ana_poscalc_t
-{
- /*! \brief
- * Type of calculation.
- *
- * This field may differ from the type requested by the user, because
- * it is changed internally to the most effective calculation.
- * For example, if the user requests a COM calculation for residues
- * consisting of single atoms, it is simply set to POS_ATOM.
- * To provide a consistent interface to the user, the field \p itype
- * should be used when information should be given out.
- */
- e_poscalc_t type;
- /*! \brief
- * Flags for calculation options.
- *
- * See \ref poscalc_flags "documentation of the flags".
- */
- int flags;
-
- /*! \brief
- * Type for the created indices.
- *
- * This field always agrees with the type that the user requested, but
- * may differ from \p type.
- */
- e_index_t itype;
- /*! \brief
- * Block data for the calculation.
- */
- t_blocka b;
- /*! \brief
- * Mapping from the blocks to the blocks of \p sbase.
- *
- * If \p sbase is NULL, this field is also.
- */
- int *baseid;
- /*! \brief
- * Maximum evaluation group.
- */
- gmx_ana_index_t gmax;
-
- /** Position storage for calculations that are used as a base. */
- gmx_ana_pos_t *p;
-
- /** TRUE if the positions have been evaluated for the current frame. */
- gmx_bool bEval;
- /*! \brief
- * Base position data for this calculation.
- *
- * If not NULL, the centers required by this calculation have
- * already been calculated in \p sbase.
- * The structure pointed by \p sbase is always a static calculation.
- */
- struct gmx_ana_poscalc_t *sbase;
- /** Next structure in the linked list of calculations. */
- struct gmx_ana_poscalc_t *next;
- /** Previous structure in the linked list of calculations. */
- struct gmx_ana_poscalc_t *prev;
- /** Number of references to this structure. */
- int refcount;
- /** Collection this calculation belongs to. */
- gmx_ana_poscalc_coll_t *coll;
-};
-
-static const char *const poscalc_enum_strings[] = {
- "atom",
- "res_com", "res_cog",
- "mol_com", "mol_cog",
- "whole_res_com", "whole_res_cog",
- "whole_mol_com", "whole_mol_cog",
- "part_res_com", "part_res_cog",
- "part_mol_com", "part_mol_cog",
- "dyn_res_com", "dyn_res_cog",
- "dyn_mol_com", "dyn_mol_cog",
- NULL,
-};
-#define NENUM asize(poscalc_enum_strings)
-
-/*! \brief
- * Returns the partition type for a given position type.
- *
- * \param [in] type \c e_poscalc_t value to convert.
- * \returns Corresponding \c e_indet_t.
- */
-static e_index_t
-index_type_for_poscalc(e_poscalc_t type)
-{
- switch(type)
- {
- case POS_ATOM: return INDEX_ATOM;
- case POS_RES: return INDEX_RES;
- case POS_MOL: return INDEX_MOL;
- case POS_ALL: return INDEX_ALL;
- case POS_ALL_PBC: return INDEX_ALL;
- }
- return INDEX_UNKNOWN;
-}
-
-/*!
- * \param[in] post String (typically an enum command-line argument).
- * Allowed values: 'atom', 'res_com', 'res_cog', 'mol_com', 'mol_cog',
- * or one of the last four prepended by 'whole_', 'part_', or 'dyn_'.
- * \param[out] type \c e_poscalc_t corresponding to \p post.
- * \param[in,out] flags Flags corresponding to \p post.
- * On input, the flags should contain the default flags.
- * On exit, the flags \ref POS_MASS, \ref POS_COMPLMAX and
- * \ref POS_COMPLWHOLE have been set according to \p post
- * (the completion flags are left at the default values if no completion
- * prefix is given).
- * \returns 0 if \p post is one of the valid strings, EINVAL otherwise.
- *
- * \attention
- * Checking is not complete, and other values than those listed above
- * may be accepted for \p post, but the results are undefined.
- */
-int
-gmx_ana_poscalc_type_from_enum(const char *post, e_poscalc_t *type, int *flags)
-{
- const char *ptr;
-
- if (post[0] == 'a')
- {
- *type = POS_ATOM;
- *flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
- return 0;
- }
-
- /* Process the prefix */
- ptr = post;
- if (post[0] == 'w')
- {
- *flags &= ~POS_COMPLMAX;
- *flags |= POS_COMPLWHOLE;
- ptr = post + 6;
- }
- else if (post[0] == 'p')
- {
- *flags &= ~POS_COMPLWHOLE;
- *flags |= POS_COMPLMAX;
- ptr = post + 5;
- }
- else if (post[0] == 'd')
- {
- *flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
- ptr = post + 4;
- }
-
- if (ptr[0] == 'r')
- {
- *type = POS_RES;
- }
- else if (ptr[0] == 'm')
- {
- *type = POS_MOL;
- }
- else
- {
- gmx_incons("unknown position calculation type");
- return EINVAL;
- }
- if (ptr[6] == 'm')
- {
- *flags |= POS_MASS;
- }
- else if (ptr[6] == 'g')
- {
- *flags &= ~POS_MASS;
- }
- else
- {
- gmx_incons("unknown position calculation type");
- return EINVAL;
- }
- return 0;
-}
-
-/*!
- * \param[in] bAtom If TRUE, the "atom" value is included.
- * \returns NULL-terminated array of strings that contains the string
- * values acceptable for gmx_ana_poscalc_type_from_enum().
- *
- * The first string in the returned list is always NULL to allow the list to
- * be used with Gromacs command-line parsing.
- */
-const char **
-gmx_ana_poscalc_create_type_enum(gmx_bool bAtom)
-{
- const char **pcenum;
- size_t i;
-
- if (bAtom)
- {
- snew(pcenum, NENUM+1);
- for (i = 0; i < NENUM; ++i)
- {
- pcenum[i+1] = poscalc_enum_strings[i];
- }
- }
- else
- {
- snew(pcenum, NENUM+1-1);
- for (i = 1; i < NENUM; ++i)
- {
- pcenum[i] = poscalc_enum_strings[i];
- }
- }
- pcenum[0] = NULL;
- return pcenum;
-}
-
-/*!
- * \param[out] pccp Allocated position calculation collection.
- * \returns 0 for success.
- */
-int
-gmx_ana_poscalc_coll_create(gmx_ana_poscalc_coll_t **pccp)
-{
- gmx_ana_poscalc_coll_t *pcc;
-
- snew(pcc, 1);
- pcc->top = NULL;
- pcc->first = NULL;
- pcc->last = NULL;
- pcc->bInit = FALSE;
- *pccp = pcc;
- return 0;
-}
-
-/*!
- * \param[in,out] pcc Position calculation collection data structure.
- * \param[in] top Topology data structure.
- *
- * This function should be called to set the topology before using
- * gmx_ana_poscalc_set_maxindex() for any calculation that requires
- * topology information.
- */
-void
-gmx_ana_poscalc_coll_set_topology(gmx_ana_poscalc_coll_t *pcc, t_topology *top)
-{
- pcc->top = top;
-}
-
-/*!
- * \param[in] pcc Position calculation collection to free.
- *
- * The pointer \p pcc is invalid after the call.
- * Any calculations in the collection are also freed, no matter how many
- * references to them are left.
- */
-void
-gmx_ana_poscalc_coll_free(gmx_ana_poscalc_coll_t *pcc)
-{
- while (pcc->first)
- {
- gmx_ana_poscalc_free(pcc->first);
- }
- sfree(pcc);
-}
-
-/*!
- * \param[in] fp File handle to receive the output.
- * \param[in] pcc Position calculation collection to print.
- *
- * The output is very technical, making this function mainly useful for
- * debugging purposes.
- */
-void
-gmx_ana_poscalc_coll_print_tree(FILE *fp, gmx_ana_poscalc_coll_t *pcc)
-{
- gmx_ana_poscalc_t *pc;
- int i, j;
-
- fprintf(fp, "Position calculations:\n");
- i = 1;
- pc = pcc->first;
- while (pc)
- {
- fprintf(fp, "%2d ", i);
- switch (pc->type)
- {
- case POS_ATOM: fprintf(fp, "ATOM"); break;
- case POS_RES: fprintf(fp, "RES"); break;
- case POS_MOL: fprintf(fp, "MOL"); break;
- case POS_ALL: fprintf(fp, "ALL"); break;
- case POS_ALL_PBC: fprintf(fp, "ALL_PBC"); break;
- }
- if (pc->itype != index_type_for_poscalc(pc->type))
- {
- fprintf(fp, " (");
- switch (pc->itype)
- {
- case INDEX_UNKNOWN: fprintf(fp, "???"); break;
- case INDEX_ATOM: fprintf(fp, "ATOM"); break;
- case INDEX_RES: fprintf(fp, "RES"); break;
- case INDEX_MOL: fprintf(fp, "MOL"); break;
- case INDEX_ALL: fprintf(fp, "ALL"); break;
- }
- fprintf(fp, ")");
- }
- fprintf(fp, " flg=");
- if (pc->flags & POS_MASS)
- {
- fprintf(fp, "M");
- }
- if (pc->flags & POS_DYNAMIC)
- {
- fprintf(fp, "D");
- }
- if (pc->flags & POS_MASKONLY)
- {
- fprintf(fp, "A");
- }
- if (pc->flags & POS_COMPLMAX)
- {
- fprintf(fp, "Cm");
- }
- if (pc->flags & POS_COMPLWHOLE)
- {
- fprintf(fp, "Cw");
- }
- if (!pc->flags)
- {
- fprintf(fp, "0");
- }
- fprintf(fp, " nr=%d nra=%d", pc->b.nr, pc->b.nra);
- fprintf(fp, " refc=%d", pc->refcount);
- fprintf(fp, "\n");
- if (pc->gmax.nalloc_index > 0)
- {
- fprintf(fp, " Group: ");
- if (pc->gmax.isize > 20)
- {
- fprintf(fp, " %d atoms", pc->gmax.isize);
- }
- else
- {
- for (j = 0; j < pc->gmax.isize; ++j)
- {
- fprintf(fp, " %d", pc->gmax.index[j] + 1);
- }
- }
- fprintf(fp, "\n");
- }
- if (pc->b.nalloc_a > 0)
- {
- fprintf(fp, " Atoms: ");
- if (pc->b.nra > 20)
- {
- fprintf(fp, " %d atoms", pc->b.nra);
- }
- else
- {
- for (j = 0; j < pc->b.nra; ++j)
- {
- fprintf(fp, " %d", pc->b.a[j] + 1);
- }
- }
- fprintf(fp, "\n");
- }
- if (pc->b.nalloc_index > 0)
- {
- fprintf(fp, " Blocks:");
- if (pc->b.nr > 20)
- {
- fprintf(fp, " %d pcs", pc->b.nr);
- }
- else
- {
- for (j = 0; j <= pc->b.nr; ++j)
- {
- fprintf(fp, " %d", pc->b.index[j]);
- }
- }
- fprintf(fp, "\n");
- }
- if (pc->sbase)
- {
- gmx_ana_poscalc_t *base;
-
- fprintf(fp, " Base: ");
- j = 1;
- base = pcc->first;
- while (base && base != pc->sbase)
- {
- ++j;
- base = base->next;
- }
- fprintf(fp, "%d", j);
- if (pc->baseid && pc->b.nr <= 20)
- {
- fprintf(fp, " id:");
- for (j = 0; j < pc->b.nr; ++j)
- {
- fprintf(fp, " %d", pc->baseid[j]+1);
- }
- }
- fprintf(fp, "\n");
- }
- ++i;
- pc = pc->next;
- }
-}
-
-/*! \brief
- * Inserts a position calculation structure into its collection.
- *
- * \param pc Data structure to insert.
- * \param before Data structure before which to insert
- * (NULL = insert at end).
- *
- * Inserts \p pc to its collection before \p before.
- * If \p before is NULL, \p pc is appended to the list.
- */
-static void
-insert_poscalc(gmx_ana_poscalc_t *pc, gmx_ana_poscalc_t *before)
-{
- if (before == NULL)
- {
- pc->next = NULL;
- pc->prev = pc->coll->last;
- if (pc->coll->last)
- {
- pc->coll->last->next = pc;
- }
- pc->coll->last = pc;
- }
- else
- {
- pc->prev = before->prev;
- pc->next = before;
- if (before->prev)
- {
- before->prev->next = pc;
- }
- before->prev = pc;
- }
- if (!pc->prev)
- {
- pc->coll->first = pc;
- }
-}
-
-/*! \brief
- * Removes a position calculation structure from its collection.
- *
- * \param pc Data structure to remove.
- *
- * Removes \p pc from its collection.
- */
-static void
-remove_poscalc(gmx_ana_poscalc_t *pc)
-{
- if (pc->prev)
- {
- pc->prev->next = pc->next;
- }
- else if (pc == pc->coll->first)
- {
- pc->coll->first = pc->next;
- }
- if (pc->next)
- {
- pc->next->prev = pc->prev;
- }
- else if (pc == pc->coll->last)
- {
- pc->coll->last = pc->prev;
- }
- pc->prev = pc->next = NULL;
-}
-
-/*! \brief
- * Initializes position calculation using the maximum possible input index.
- *
- * \param[in,out] pc Position calculation data structure.
- * \param[in] g Maximum index group for the calculation.
- * \param[in] bBase Whether \p pc will be used as a base or not.
- *
- * \p bBase affects on how the \p pc->gmax field is initialized.
- */
-static void
-set_poscalc_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g, gmx_bool bBase)
-{
- gmx_ana_index_make_block(&pc->b, pc->coll->top, g, pc->itype, pc->flags & POS_COMPLWHOLE);
- /* Set the type to POS_ATOM if the calculation in fact is such. */
- if (pc->b.nr == pc->b.nra)
- {
- pc->type = POS_ATOM;
- pc->flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
- }
- /* Set the POS_COMPLWHOLE flag if the calculation in fact always uses
- * complete residues and molecules. */
- if (!(pc->flags & POS_COMPLWHOLE)
- && (!(pc->flags & POS_DYNAMIC) || (pc->flags & POS_COMPLMAX))
- && (pc->type == POS_RES || pc->type == POS_MOL)
- && gmx_ana_index_has_complete_elems(g, pc->itype, pc->coll->top))
- {
- pc->flags &= ~POS_COMPLMAX;
- pc->flags |= POS_COMPLWHOLE;
- }
- /* Setup the gmax field */
- if ((pc->flags & POS_COMPLWHOLE) && !bBase && pc->b.nra > g->isize)
- {
- gmx_ana_index_copy(&pc->gmax, g, TRUE);
- sfree(pc->gmax.name);
- pc->gmax.name = NULL;
- }
- else
- {
- gmx_ana_index_set(&pc->gmax, pc->b.nra, pc->b.a, NULL, 0);
- }
-}
-
-/*! \brief
- * Checks whether a position calculation should use a base at all.
- *
- * \param[in] pc Position calculation data to check.
- * \returns TRUE if \p pc can use a base and gets some benefit out of it,
- * FALSE otherwise.
- */
-static gmx_bool
-can_use_base(gmx_ana_poscalc_t *pc)
-{
- /* For atoms, it should be faster to do a simple copy, so don't use a
- * base. */
- if (pc->type == POS_ATOM)
- {
- return FALSE;
- }
- /* For dynamic selections that do not use completion, it is not possible
- * to use a base. */
- if ((pc->type == POS_RES || pc->type == POS_MOL)
- && (pc->flags & POS_DYNAMIC) && !(pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
- {
- return FALSE;
- }
- /* Dynamic calculations for a single position cannot use a base. */
- if ((pc->type == POS_ALL || pc->type == POS_ALL_PBC)
- && (pc->flags & POS_DYNAMIC))
- {
- return FALSE;
- }
- return TRUE;
-}
-
-/*! \brief
- * Checks whether two position calculations should use a common base.
- *
- * \param[in] pc1 Calculation 1 to check for.
- * \param[in] pc2 Calculation 2 to check for.
- * \param[in] g1 Index group structure that contains the atoms from
- * \p pc1.
- * \param[in,out] g Working space, should have enough allocated memory to
- * contain the intersection of the atoms in \p pc1 and \p pc2.
- * \returns TRUE if the two calculations should be merged to use a common
- * base, FALSE otherwise.
- */
-static gmx_bool
-should_merge(gmx_ana_poscalc_t *pc1, gmx_ana_poscalc_t *pc2,
- gmx_ana_index_t *g1, gmx_ana_index_t *g)
-{
- gmx_ana_index_t g2;
-
- /* Do not merge calculations with different mass weighting. */
- if ((pc1->flags & POS_MASS) != (pc2->flags & POS_MASS))
- {
- return FALSE;
- }
- /* Avoid messing up complete calculations. */
- if ((pc1->flags & POS_COMPLWHOLE) != (pc2->flags & POS_COMPLWHOLE))
- {
- return FALSE;
- }
- /* Find the overlap between the calculations. */
- gmx_ana_index_set(&g2, pc2->b.nra, pc2->b.a, NULL, 0);
- gmx_ana_index_intersection(g, g1, &g2);
- /* Do not merge if there is no overlap. */
- if (g->isize == 0)
- {
- return FALSE;
- }
- /* Full completion calculations always match if the type is correct. */
- if ((pc1->flags & POS_COMPLWHOLE) && (pc2->flags & POS_COMPLWHOLE)
- && pc1->type == pc2->type)
- {
- return TRUE;
- }
- /* The calculations also match if the intersection consists of full
- * blocks. */
- if (gmx_ana_index_has_full_ablocks(g, &pc1->b)
- && gmx_ana_index_has_full_ablocks(g, &pc2->b))
- {
- return TRUE;
- }
- return FALSE;
-}
-
-/*! \brief
- * Creates a static base for position calculation.
- *
- * \param pc Data structure to copy.
- * \returns Pointer to a newly allocated base for \p pc.
- *
- * Creates and returns a deep copy of \p pc, but clears the
- * \ref POS_DYNAMIC and \ref POS_MASKONLY flags.
- * The newly created structure is set as the base (\c gmx_ana_poscalc_t::sbase)
- * of \p pc and inserted in the collection before \p pc.
- */
-static gmx_ana_poscalc_t *
-create_simple_base(gmx_ana_poscalc_t *pc)
-{
- gmx_ana_poscalc_t *base;
- int flags;
- int rc;
-
- flags = pc->flags & ~(POS_DYNAMIC | POS_MASKONLY);
- rc = gmx_ana_poscalc_create(&base, pc->coll, pc->type, flags);
- if (rc != 0)
- {
- gmx_fatal(FARGS, "position calculation base creation failed");
- }
- set_poscalc_maxindex(base, &pc->gmax, TRUE);
-
- snew(base->p, 1);
-
- pc->sbase = base;
- remove_poscalc(base);
- insert_poscalc(base, pc);
-
- return base;
-}
-
-/*! \brief
- * Merges a calculation into another calculation such that the new calculation
- * can be used as a base.
- *
- * \param[in,out] base Base calculation to merge to.
- * \param[in,out] pc Position calculation to merge to \p base.
- *
- * After the call, \p base can be used as a base for \p pc (or any calculation
- * that used it as a base).
- * It is assumed that any overlap between \p base and \p pc is in complete
- * blocks, i.e., that the merge is possible.
- */
-static void
-merge_to_base(gmx_ana_poscalc_t *base, gmx_ana_poscalc_t *pc)
-{
- gmx_ana_index_t gp, gb, g;
- int isize, bnr;
- int i, j, bi, bj, bo;
-
- base->flags |= pc->flags & (POS_VELOCITIES | POS_FORCES);
- gmx_ana_index_set(&gp, pc->b.nra, pc->b.a, NULL, 0);
- gmx_ana_index_set(&gb, base->b.nra, base->b.a, NULL, 0);
- isize = gmx_ana_index_difference_size(&gp, &gb);
- if (isize > 0)
- {
- gmx_ana_index_clear(&g);
- gmx_ana_index_reserve(&g, base->b.nra + isize);
- /* Find the new blocks */
- gmx_ana_index_difference(&g, &gp, &gb);
- /* Count the blocks in g */
- i = bi = bnr = 0;
- while (i < g.isize)
- {
- while (pc->b.a[pc->b.index[bi]] != g.index[i])
- {
- ++bi;
- }
- i += pc->b.index[bi+1] - pc->b.index[bi];
- ++bnr;
- ++bi;
- }
- /* Merge the atoms into a temporary structure */
- gmx_ana_index_merge(&g, &gb, &g);
- /* Merge the blocks */
- srenew(base->b.index, base->b.nr + bnr + 1);
- i = g.isize - 1;
- bi = base->b.nr - 1;
- bj = pc->b.nr - 1;
- bo = base->b.nr + bnr - 1;
- base->b.index[bo+1] = i + 1;
- while (bo >= 0)
- {
- if (bi < 0 || base->b.a[base->b.index[bi+1]-1] != g.index[i])
- {
- i -= pc->b.index[bj+1] - pc->b.index[bj];
- --bj;
- }
- else
- {
- if (bj >= 0 && pc->b.a[pc->b.index[bj+1]-1] == g.index[i])
- {
- --bj;
- }
- i -= base->b.index[bi+1] - base->b.index[bi];
- --bi;
- }
- base->b.index[bo] = i + 1;
- --bo;
- }
- base->b.nr += bnr;
- base->b.nalloc_index += bnr;
- sfree(base->b.a);
- base->b.nra = g.isize;
- base->b.a = g.index;
- base->b.nalloc_a = g.isize;
- /* Refresh the gmax field */
- gmx_ana_index_set(&base->gmax, base->b.nra, base->b.a, NULL, 0);
- }
-}
-
-/*! \brief
- * Merges two bases into one.
- *
- * \param[in,out] tbase Base calculation to merge to.
- * \param[in] mbase Base calculation to merge to \p tbase.
- *
- * After the call, \p mbase has been freed and \p tbase is used as the base
- * for all calculations that previously had \p mbase as their base.
- * It is assumed that any overlap between \p tbase and \p mbase is in complete
- * blocks, i.e., that the merge is possible.
- */
-static void
-merge_bases(gmx_ana_poscalc_t *tbase, gmx_ana_poscalc_t *mbase)
-{
- gmx_ana_poscalc_t *pc;
-
- merge_to_base(tbase, mbase);
- remove_poscalc(mbase);
- /* Set tbase as the base for all calculations that had mbase */
- pc = tbase->coll->first;
- while (pc)
- {
- if (pc->sbase == mbase)
- {
- pc->sbase = tbase;
- tbase->refcount++;
- }
- pc = pc->next;
- }
- /* Free mbase */
- mbase->refcount = 0;
- gmx_ana_poscalc_free(mbase);
-}
-
-/*! \brief
- * Setups the static base calculation for a position calculation.
- *
- * \param[in,out] pc Position calculation to setup the base for.
- */
-static void
-setup_base(gmx_ana_poscalc_t *pc)
-{
- gmx_ana_poscalc_t *base, *pbase, *next;
- gmx_ana_index_t gp, g;
-
- /* Exit immediately if pc should not have a base. */
- if (!can_use_base(pc))
- {
- return;
- }
-
- gmx_ana_index_set(&gp, pc->b.nra, pc->b.a, NULL, 0);
- gmx_ana_index_clear(&g);
- gmx_ana_index_reserve(&g, pc->b.nra);
- pbase = pc;
- base = pc->coll->first;
- while (base)
- {
- /* Save the next calculation so that we can safely delete base */
- next = base->next;
- /* Skip pc, calculations that already have a base (we should match the
- * base instead), as well as calculations that should not have a base.
- * If the above conditions are met, check whether we should do a
- * merge.
- */
- if (base != pc && !base->sbase && can_use_base(base)
- && should_merge(pbase, base, &gp, &g))
- {
- /* Check whether this is the first base found */
- if (pbase == pc)
- {
- /* Create a real base if one is not present */
- if (!base->p)
- {
- pbase = create_simple_base(base);
- }
- else
- {
- pbase = base;
- }
- /* Make it a base for pc as well */
- merge_to_base(pbase, pc);
- pc->sbase = pbase;
- pbase->refcount++;
- }
- else /* This was not the first base */
- {
- if (!base->p)
- {
- /* If it is not a real base, just make the new base as
- * the base for it as well. */
- merge_to_base(pbase, base);
- base->sbase = pbase;
- pbase->refcount++;
- }
- else
- {
- /* If base is a real base, merge it with the new base
- * and delete it. */
- merge_bases(pbase, base);
- }
- }
- gmx_ana_index_set(&gp, pbase->b.nra, pbase->b.a, NULL, 0);
- gmx_ana_index_reserve(&g, pc->b.nra);
- }
- /* Proceed to the next unchecked calculation */
- base = next;
- }
-
- gmx_ana_index_deinit(&g);
-
- /* If no base was found, create one if one is required */
- if (!pc->sbase && (pc->flags & POS_DYNAMIC)
- && (pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
- {
- create_simple_base(pc);
- }
-}
-
-/*!
- * \param[out] pcp Position calculation data structure pointer to initialize.
- * \param[in,out] pcc Position calculation collection.
- * \param[in] type Type of calculation.
- * \param[in] flags Flags for setting calculation options
- * (see \ref poscalc_flags "documentation of the flags").
- * \returns 0 on success.
- */
-int
-gmx_ana_poscalc_create(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
- e_poscalc_t type, int flags)
-{
- gmx_ana_poscalc_t *pc;
-
- snew(pc, 1);
- pc->type = type;
- pc->itype = index_type_for_poscalc(type);
- gmx_ana_poscalc_set_flags(pc, flags);
- pc->refcount = 1;
- pc->coll = pcc;
- insert_poscalc(pc, NULL);
- *pcp = pc;
- return 0;
-}
-
-/*!
- * \param[out] pcp Position calculation data structure pointer to initialize.
- * \param[in,out] pcc Position calculation collection.
- * \param[in] post One of the strings acceptable for
- * gmx_ana_poscalc_type_from_enum().
- * \param[in] flags Flags for setting calculation options
- * (see \ref poscalc_flags "documentation of the flags").
- * \returns 0 on success, a non-zero error value on error.
- *
- * This is a convenience wrapper for gmx_ana_poscalc_create().
- * \p flags sets the default calculation options if not overridden by \p post;
- * see gmx_ana_poscalc_type_from_enum().
- *
- * \see gmx_ana_poscalc_create(), gmx_ana_poscalc_type_from_enum()
- */
-int
-gmx_ana_poscalc_create_enum(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
- const char *post, int flags)
-{
- e_poscalc_t type;
- int cflags;
- int rc;
-
- cflags = flags;
- rc = gmx_ana_poscalc_type_from_enum(post, &type, &cflags);
- if (rc != 0)
- {
- *pcp = NULL;
- return rc;
- }
- return gmx_ana_poscalc_create(pcp, pcc, type, cflags);
-}
-
-/*!
- * \param[in,out] pc Position calculation data structure.
- * \param[in] flags New flags.
- *
- * \p flags are added to the old flags.
- * If calculation type is \ref POS_ATOM, \ref POS_MASS is automatically
- * cleared.
- * If both \ref POS_DYNAMIC and \ref POS_MASKONLY are provided,
- * \ref POS_DYNAMIC is cleared.
- * If calculation type is not \ref POS_RES or \ref POS_MOL,
- * \ref POS_COMPLMAX and \ref POS_COMPLWHOLE are automatically cleared.
- */
-void
-gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t *pc, int flags)
-{
- if (pc->type == POS_ATOM)
- {
- flags &= ~POS_MASS;
- }
- if (flags & POS_MASKONLY)
- {
- flags &= ~POS_DYNAMIC;
- }
- if (pc->type != POS_RES && pc->type != POS_MOL)
- {
- flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
- }
- pc->flags |= flags;
-}
-
-/*!
- * \param[in,out] pc Position calculation data structure.
- * \param[in] g Maximum index group for the calculation.
- *
- * Subsequent calls to gmx_ana_poscalc_update() should use only subsets of
- * \p g for evaluation.
- *
- * The topology should have been set for the collection of which \p pc is
- * a member.
- */
-void
-gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g)
-{
- set_poscalc_maxindex(pc, g, FALSE);
- setup_base(pc);
-}
-
-/*!
- * \param[in] pc Position calculation data structure.
- * \param[out] p Output positions.
- *
- * Calls to gmx_ana_poscalc_update() using \p pc should use only positions
- * initialized with this function.
- * The \c p->g pointer is initialized to point to an internal group that
- * contains the maximum index group set with gmx_ana_poscalc_set_maxindex().
- */
-void
-gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t *pc, gmx_ana_pos_t *p)
-{
- gmx_ana_indexmap_init(&p->m, &pc->gmax, pc->coll->top, pc->itype);
- if (!(pc->flags & POS_DYNAMIC))
- {
- gmx_ana_indexmap_set_static(&p->m, &pc->b);
- }
- gmx_ana_pos_reserve(p, p->m.nr, 0);
- if (pc->flags & POS_VELOCITIES)
- {
- gmx_ana_pos_reserve_velocities(p);
- }
- if (pc->flags & POS_FORCES)
- {
- gmx_ana_pos_reserve_forces(p);
- }
- gmx_ana_pos_set_nr(p, p->m.nr);
- gmx_ana_pos_set_evalgrp(p, &pc->gmax);
-}
-
-/*!
- * \param pc Position calculation data to be freed.
- *
- * The \p pc pointer is invalid after the call.
- */
-void
-gmx_ana_poscalc_free(gmx_ana_poscalc_t *pc)
-{
- if (!pc)
- {
- return;
- }
-
- pc->refcount--;
- if (pc->refcount > 0)
- {
- return;
- }
-
- remove_poscalc(pc);
- if (pc->b.nalloc_index > 0)
- {
- sfree(pc->b.index);
- }
- if (pc->b.nalloc_a > 0)
- {
- sfree(pc->b.a);
- }
- if (pc->flags & POS_COMPLWHOLE)
- {
- gmx_ana_index_deinit(&pc->gmax);
- }
- if (pc->p)
- {
- gmx_ana_pos_free(pc->p);
- }
- if (pc->sbase)
- {
- gmx_ana_poscalc_free(pc->sbase);
- sfree(pc->baseid);
- }
- sfree(pc);
-}
-
-/*!
- * \param[in] pc Position calculation data to query.
- * \returns TRUE if \p pc requires topology for initialization and/or
- * evaluation, FALSE otherwise.
- */
-gmx_bool
-gmx_ana_poscalc_requires_top(gmx_ana_poscalc_t *pc)
-{
- if ((pc->flags & POS_MASS) || pc->type == POS_RES || pc->type == POS_MOL)
- {
- return TRUE;
- }
- return FALSE;
-}
-
-/*!
- * \param[in,out] pcc Position calculation collection to initialize.
- *
- * This function does some final initialization of the data structures in the
- * collection to prepare them for evaluation.
- * After this function has been called, it is no longer possible to add new
- * calculations to the collection.
- *
- * This function is automatically called by gmx_ana_poscalc_init_frame()
- * if not called by the user earlier.
- * Multiple calls to the function are ignored.
- */
-void
-gmx_ana_poscalc_init_eval(gmx_ana_poscalc_coll_t *pcc)
-{
- gmx_ana_poscalc_t *pc;
- int bi, bj;
-
- if (pcc->bInit)
- {
- return;
- }
- pc = pcc->first;
- while (pc)
- {
- /* Initialize position storage for base calculations */
- if (pc->p)
- {
- gmx_ana_poscalc_init_pos(pc, pc->p);
- }
- /* Construct the mapping of the base positions */
- if (pc->sbase)
- {
- snew(pc->baseid, pc->b.nr);
- for (bi = bj = 0; bi < pc->b.nr; ++bi, ++bj)
- {
- while (pc->sbase->b.a[pc->sbase->b.index[bj]] != pc->b.a[pc->b.index[bi]])
- {
- ++bj;
- }
- pc->baseid[bi] = bj;
- }
- }
- /* Free the block data for dynamic calculations */
- if (pc->flags & POS_DYNAMIC)
- {
- if (pc->b.nalloc_index > 0)
- {
- sfree(pc->b.index);
- pc->b.nalloc_index = 0;
- }
- if (pc->b.nalloc_a > 0)
- {
- sfree(pc->b.a);
- pc->b.nalloc_a = 0;
- }
- }
- pc = pc->next;
- }
- pcc->bInit = TRUE;
-}
-
-/*!
- * \param[in,out] pcc Position calculation collection to initialize.
- *
- * Clears the evaluation flag for all calculations.
- * Should be called for each frame before calling gmx_ana_poscalc_update().
- *
- * This function is automatically called by gmx_ana_do() for each
- * frame, and should not be called by the user unless gmx_ana_do() is
- * not being used.
- *
- * This function calls gmx_ana_poscalc_init_eval() automatically if it has
- * not been called earlier.
- */
-void
-gmx_ana_poscalc_init_frame(gmx_ana_poscalc_coll_t *pcc)
-{
- gmx_ana_poscalc_t *pc;
-
- if (!pcc->bInit)
- {
- gmx_ana_poscalc_init_eval(pcc);
- }
- /* Clear the evaluation flags */
- pc = pcc->first;
- while (pc)
- {
- pc->bEval = FALSE;
- pc = pc->next;
- }
-}
-
-/*!
- * \param[in] pc Position calculation data.
- * \param[in,out] p Output positions, initialized previously with
- * gmx_ana_poscalc_init_pos() using \p pc.
- * \param[in] g Index group to use for the update.
- * \param[in] fr Current frame.
- * \param[in] pbc PBC data, or NULL if no PBC should be used.
- *
- * gmx_ana_poscalc_init_frame() should be called for each frame before calling
- * this function.
- */
-void
-gmx_ana_poscalc_update(gmx_ana_poscalc_t *pc, gmx_ana_pos_t *p,
- gmx_ana_index_t *g, t_trxframe *fr, t_pbc *pbc)
-{
- int i, j, bi, bj;
-
- if (pc->bEval == TRUE && !(pc->flags & POS_MASKONLY))
- {
- return;
- }
- if (pc->sbase)
- {
- gmx_ana_poscalc_update(pc->sbase, NULL, NULL, fr, pbc);
- }
- if (!p)
- {
- p = pc->p;
- }
- if (!g)
- {
- g = &pc->gmax;
- }
- gmx_ana_pos_set_evalgrp(p, g);
-
- /* Update the index map */
- if (pc->flags & POS_DYNAMIC)
- {
- gmx_ana_indexmap_update(&p->m, g, FALSE);
- p->nr = p->m.nr;
- }
- else if (pc->flags & POS_MASKONLY)
- {
- gmx_ana_indexmap_update(&p->m, g, TRUE);
- if (pc->bEval)
- return;
- }
- if (!(pc->flags & POS_DYNAMIC))
- {
- pc->bEval = TRUE;
- }
-
- /* Evaluate the positions */
- if (pc->sbase)
- {
- /* TODO: It might be faster to evaluate the positions within this
- * loop instead of in the beginning. */
- if (pc->flags & POS_DYNAMIC)
- {
- for (bi = 0; bi < p->nr; ++bi)
- {
- bj = pc->baseid[p->m.refid[bi]];
- copy_rvec(pc->sbase->p->x[bj], p->x[bi]);
- }
- if (p->v)
- {
- for (bi = 0; bi < p->nr; ++bi)
- {
- bj = pc->baseid[p->m.refid[bi]];
- copy_rvec(pc->sbase->p->v[bj], p->v[bi]);
- }
- }
- if (p->f)
- {
- for (bi = 0; bi < p->nr; ++bi)
- {
- bj = pc->baseid[p->m.refid[bi]];
- copy_rvec(pc->sbase->p->f[bj], p->f[bi]);
- }
- }
- }
- else
- {
- for (bi = 0; bi < p->nr; ++bi)
- {
- bj = pc->baseid[bi];
- copy_rvec(pc->sbase->p->x[bj], p->x[bi]);
- }
- if (p->v)
- {
- for (bi = 0; bi < p->nr; ++bi)
- {
- bj = pc->baseid[bi];
- copy_rvec(pc->sbase->p->v[bj], p->v[bi]);
- }
- }
- if (p->f)
- {
- for (bi = 0; bi < p->nr; ++bi)
- {
- bj = pc->baseid[bi];
- copy_rvec(pc->sbase->p->f[bj], p->f[bi]);
- }
- }
- }
- }
- else /* pc->sbase is NULL */
- {
- if (pc->flags & POS_DYNAMIC)
- {
- pc->b.nr = p->m.mapb.nr;
- pc->b.index = p->m.mapb.index;
- pc->b.nra = g->isize;
- pc->b.a = g->index;
- }
- if (p->v && !fr->bV)
- {
- for (i = 0; i < pc->b.nra; ++i)
- {
- clear_rvec(p->v[i]);
- }
- }
- if (p->f && !fr->bF)
- {
- for (i = 0; i < pc->b.nra; ++i)
- {
- clear_rvec(p->f[i]);
- }
- }
- /* Here, we assume that the topology has been properly initialized,
- * and do not check the return values of gmx_calc_comg*(). */
- switch (pc->type)
- {
- case POS_ATOM:
- for (i = 0; i < pc->b.nra; ++i)
- {
- copy_rvec(fr->x[pc->b.a[i]], p->x[i]);
- }
- if (p->v && fr->bV)
- {
- for (i = 0; i < pc->b.nra; ++i)
- {
- copy_rvec(fr->v[pc->b.a[i]], p->v[i]);
- }
- }
- if (p->f && fr->bF)
- {
- for (i = 0; i < pc->b.nra; ++i)
- {
- copy_rvec(fr->f[pc->b.a[i]], p->f[i]);
- }
- }
- break;
- case POS_ALL:
- gmx_calc_comg(pc->coll->top, fr->x, pc->b.nra, pc->b.a,
- pc->flags & POS_MASS, p->x[0]);
- if (p->v && fr->bV)
- {
- gmx_calc_comg(pc->coll->top, fr->v, pc->b.nra, pc->b.a,
- pc->flags & POS_MASS, p->v[0]);
- }
- if (p->f && fr->bF)
- {
- gmx_calc_comg_f(pc->coll->top, fr->f, pc->b.nra, pc->b.a,
- pc->flags & POS_MASS, p->f[0]);
- }
- break;
- case POS_ALL_PBC:
- gmx_calc_comg_pbc(pc->coll->top, fr->x, pbc, pc->b.nra, pc->b.a,
- pc->flags & POS_MASS, p->x[0]);
- if (p->v && fr->bV)
- {
- gmx_calc_comg(pc->coll->top, fr->v, pc->b.nra, pc->b.a,
- pc->flags & POS_MASS, p->v[0]);
- }
- if (p->f && fr->bF)
- {
- gmx_calc_comg_f(pc->coll->top, fr->f, pc->b.nra, pc->b.a,
- pc->flags & POS_MASS, p->f[0]);
- }
- break;
- default:
- gmx_calc_comg_blocka(pc->coll->top, fr->x, &pc->b,
- pc->flags & POS_MASS, p->x);
- if (p->v && fr->bV)
- {
- gmx_calc_comg_blocka(pc->coll->top, fr->v, &pc->b,
- pc->flags & POS_MASS, p->v);
- }
- if (p->f && fr->bF)
- {
- gmx_calc_comg_blocka(pc->coll->top, fr->f, &pc->b,
- pc->flags & POS_MASS, p->f);
- }
- break;
- }
- }
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \internal \file
- * \brief Implementation of functions in position.h.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include <string.h>
-
-#include <smalloc.h>
-#include <typedefs.h>
-#include <vec.h>
-
-#include <indexutil.h>
-#include <position.h>
-
-/*!
- * \param[out] pos Output structure.
- *
- * Any contents of \p pos are discarded without freeing.
- */
-void
-gmx_ana_pos_clear(gmx_ana_pos_t *pos)
-{
- pos->nr = 0;
- pos->x = NULL;
- pos->v = NULL;
- pos->f = NULL;
- gmx_ana_indexmap_clear(&pos->m);
- pos->g = NULL;
- pos->nalloc_x = 0;
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- * \param[in] n Maximum number of positions.
- * \param[in] isize Maximum number of atoms.
- *
- * Ensures that enough memory is allocated in \p pos to calculate \p n
- * positions from \p isize atoms.
- */
-void
-gmx_ana_pos_reserve(gmx_ana_pos_t *pos, int n, int isize)
-{
- if (pos->nalloc_x < n)
- {
- pos->nalloc_x = n;
- srenew(pos->x, n);
- if (pos->v)
- {
- srenew(pos->v, n);
- }
- if (pos->f)
- {
- srenew(pos->f, n);
- }
- }
- if (isize > 0)
- {
- gmx_ana_indexmap_reserve(&pos->m, n, isize);
- }
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * Currently, this function can only be called after gmx_ana_pos_reserve()
- * has been called at least once with a \p n > 0.
- */
-void
-gmx_ana_pos_reserve_velocities(gmx_ana_pos_t *pos)
-{
- assert(pos->nalloc_x > 0);
- if (!pos->v)
- {
- snew(pos->v, pos->nalloc_x);
- }
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * Currently, this function can only be called after gmx_ana_pos_reserve()
- * has been called at least once with a \p n > 0.
- */
-void
-gmx_ana_pos_reserve_forces(gmx_ana_pos_t *pos)
-{
- assert(pos->nalloc_x > 0);
- if (!pos->f)
- {
- snew(pos->f, pos->nalloc_x);
- }
-}
-
-/*!
- * \param[out] pos Position data structure to initialize.
- * \param[in] x Position vector to use.
- */
-void
-gmx_ana_pos_init_const(gmx_ana_pos_t *pos, rvec x)
-{
- gmx_ana_pos_clear(pos);
- pos->nr = 1;
- snew(pos->x, 1);
- snew(pos->v, 1);
- snew(pos->f, 1);
- pos->nalloc_x = 1;
- copy_rvec(x, pos->x[0]);
- clear_rvec(pos->v[0]);
- clear_rvec(pos->f[0]);
- gmx_ana_indexmap_init(&pos->m, NULL, NULL, INDEX_UNKNOWN);
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * Frees any memory allocated within \p pos.
- * The pointer \p pos itself is not freed.
- *
- * \see gmx_ana_pos_free()
- */
-void
-gmx_ana_pos_deinit(gmx_ana_pos_t *pos)
-{
- pos->nr = 0;
- sfree(pos->x); pos->x = NULL;
- sfree(pos->v); pos->v = NULL;
- sfree(pos->f); pos->f = NULL;
- pos->nalloc_x = 0;
- gmx_ana_indexmap_deinit(&pos->m);
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * Frees any memory allocated for \p pos.
- * The pointer \p pos is also freed, and is invalid after the call.
- *
- * \see gmx_ana_pos_deinit()
- */
-void
-gmx_ana_pos_free(gmx_ana_pos_t *pos)
-{
- gmx_ana_pos_deinit(pos);
- sfree(pos);
-}
-
-/*!
- * \param[in,out] dest Destination positions.
- * \param[in] src Source positions.
- * \param[in] bFirst If TRUE, memory is allocated for \p dest and a full
- * copy is made; otherwise, only variable parts are copied, and no memory
- * is allocated.
- *
- * \p dest should have been initialized somehow (calloc() is enough).
- */
-void
-gmx_ana_pos_copy(gmx_ana_pos_t *dest, gmx_ana_pos_t *src, gmx_bool bFirst)
-{
- if (bFirst)
- {
- gmx_ana_pos_reserve(dest, src->nr, 0);
- if (src->v)
- {
- gmx_ana_pos_reserve_velocities(dest);
- }
- if (src->f)
- {
- gmx_ana_pos_reserve_forces(dest);
- }
- }
- dest->nr = src->nr;
- memcpy(dest->x, src->x, dest->nr*sizeof(*dest->x));
- if (dest->v)
- {
- memcpy(dest->v, src->v, dest->nr*sizeof(*dest->v));
- }
- if (dest->f)
- {
- memcpy(dest->f, src->f, dest->nr*sizeof(*dest->f));
- }
- gmx_ana_indexmap_copy(&dest->m, &src->m, bFirst);
- dest->g = src->g;
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- * \param[in] nr Number of positions.
- */
-void
-gmx_ana_pos_set_nr(gmx_ana_pos_t *pos, int nr)
-{
- pos->nr = nr;
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- * \param g Evaluation group.
- *
- * The old group, if any, is discarded.
- * Note that only a pointer to \p g is stored; it is the responsibility of
- * the caller to ensure that \p g is not freed while it can be accessed
- * through \p pos.
- */
-void
-gmx_ana_pos_set_evalgrp(gmx_ana_pos_t *pos, gmx_ana_index_t *g)
-{
- pos->g = g;
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * Sets the number of positions to 0.
- */
-void
-gmx_ana_pos_empty_init(gmx_ana_pos_t *pos)
-{
- pos->nr = 0;
- pos->m.nr = 0;
- pos->m.mapb.nr = 0;
- pos->m.b.nr = 0;
- pos->m.b.nra = 0;
- /* This should not really be necessary, but do it for safety... */
- pos->m.mapb.index[0] = 0;
- pos->m.b.index[0] = 0;
- /* This function should only be used to construct all the possible
- * positions, so the result should always be static. */
- pos->m.bStatic = TRUE;
- pos->m.bMapStatic = TRUE;
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * Sets the number of positions to 0.
- */
-void
-gmx_ana_pos_empty(gmx_ana_pos_t *pos)
-{
- pos->nr = 0;
- pos->m.nr = 0;
- pos->m.mapb.nr = 0;
- /* This should not really be necessary, but do it for safety... */
- pos->m.mapb.index[0] = 0;
- /* We set the flags to TRUE, although really in the empty state they
- * should be FALSE. This makes it possible to update the flags in
- * gmx_ana_pos_append(), and just make a simple check in
- * gmx_ana_pos_append_finish(). */
- pos->m.bStatic = TRUE;
- pos->m.bMapStatic = TRUE;
-}
-
-/*!
- * \param[in,out] dest Data structure to which the new position is appended.
- * \param[in,out] g Data structure to which the new atoms are appended.
- * \param[in] src Data structure from which the position is copied.
- * \param[in] i Index in \p from to copy.
- */
-void
-gmx_ana_pos_append_init(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
- gmx_ana_pos_t *src, int i)
-{
- int j, k;
-
- j = dest->nr;
- copy_rvec(src->x[i], dest->x[j]);
- if (dest->v)
- {
- if (src->v)
- {
- copy_rvec(src->v[i], dest->v[j]);
- }
- else
- {
- clear_rvec(dest->v[j]);
- }
- }
- if (dest->f)
- {
- if (src->f)
- {
- copy_rvec(src->f[i], dest->f[j]);
- }
- else
- {
- clear_rvec(dest->f[j]);
- }
- }
- dest->m.refid[j] = j;
- dest->m.mapid[j] = src->m.mapid[i];
- dest->m.orgid[j] = src->m.orgid[i];
- for (k = src->m.mapb.index[i]; k < src->m.mapb.index[i+1]; ++k)
- {
- g->index[g->isize++] = src->g->index[k];
- dest->m.b.a[dest->m.b.nra++] = src->m.b.a[k];
- }
- dest->m.mapb.index[j+1] = g->isize;
- dest->m.b.index[j+1] = g->isize;
- dest->nr++;
- dest->m.nr = dest->nr;
- dest->m.mapb.nr = dest->nr;
- dest->m.b.nr = dest->nr;
-}
-
-/*!
- * \param[in,out] dest Data structure to which the new position is appended
- * (can be NULL, in which case only \p g is updated).
- * \param[in,out] g Data structure to which the new atoms are appended.
- * \param[in] src Data structure from which the position is copied.
- * \param[in] i Index in \p src to copy.
- * \param[in] refid Reference ID in \p out
- * (all negative values are treated as -1).
- *
- * If \p dest is NULL, the value of \p refid is not used.
- */
-void
-gmx_ana_pos_append(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
- gmx_ana_pos_t *src, int i, int refid)
-{
- int j, k;
-
- for (k = src->m.mapb.index[i]; k < src->m.mapb.index[i+1]; ++k)
- {
- g->index[g->isize++] = src->g->index[k];
- }
- if (dest)
- {
- j = dest->nr;
- if (dest->v)
- {
- if (src->v)
- {
- copy_rvec(src->v[i], dest->v[j]);
- }
- else
- {
- clear_rvec(dest->v[j]);
- }
- }
- if (dest->f)
- {
- if (src->f)
- {
- copy_rvec(src->f[i], dest->f[j]);
- }
- else
- {
- clear_rvec(dest->f[j]);
- }
- }
- copy_rvec(src->x[i], dest->x[j]);
- if (refid < 0)
- {
- dest->m.refid[j] = -1;
- dest->m.bStatic = FALSE;
- /* If we are using masks, there is no need to alter the
- * mapid field. */
- }
- else
- {
- if (refid != j)
- {
- dest->m.bStatic = FALSE;
- dest->m.bMapStatic = FALSE;
- }
- dest->m.refid[j] = refid;
- /* Use the original IDs from the output structure to correctly
- * handle user customization. */
- dest->m.mapid[j] = dest->m.orgid[refid];
- }
- dest->m.mapb.index[j+1] = g->isize;
- dest->nr++;
- dest->m.nr = dest->nr;
- dest->m.mapb.nr = dest->nr;
- }
-}
-
-/*!
- * \param[in,out] pos Position data structure.
- *
- * After gmx_ana_pos_empty(), internal state of the position data structure
- * is not consistent before this function is called. This function should be
- * called after any gmx_ana_pos_append() calls have been made.
- */
-void
-gmx_ana_pos_append_finish(gmx_ana_pos_t *pos)
-{
- if (pos->m.nr != pos->m.b.nr)
- {
- pos->m.bStatic = FALSE;
- pos->m.bMapStatic = FALSE;
- }
-}
+++ /dev/null
-/*
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- */
-/*! \page libtrajana Library for trajectory analysis
- *
- * This is a trajectory analysis library for Gromacs.
- *
- * The main features of the library are:
- * - \subpage selengine "Flexible handling of textual selections" that can
- * also be dynamic, i.e., depend of the trajectory frame through
- * positions of the particles.
- * Selections evaluate directly to positions, which can then be used in
- * the analysis program.
- * - \subpage selmethods "Custom selection keywords" can also be easily
- * implemented, also on a tool-by-tool basis.
- * - Efficient \subpage nbsearch "neighborhood searching"
- * (currently not very efficient...).
- * - \subpage displacements "On-the-fly calculation of displacement vectors"
- * during a single pass over the trajectory.
- * - \subpage histograms "Calculation of histograms" with error estimates
- * through block averaging.
- *
- * The functions also automate several things common to most analysis programs
- * such as making molecules whole if required, reading input files, and
- * setting up index groups for analysis.
- * The library unifies the structure of analysis programs (at least a bit)
- * and makes it easier to add common functionality to all analysis programs.
- *
- *
- * \section main_using Using the library
- *
- * There is a \ref share/template/template.c "template" for writing
- * analysis programs, the documentation for it and links from there should
- * help getting started.
- *
- *
- * \internal
- * \section libtrajana_impl Implementation details
- *
- * Some internal implementation details of the library are documented on
- * separate pages:
- * - \subpage poscalcengine
- * - \subpage selparser
- * - \subpage selcompiler
- */
-/*! \page selengine Text-based selections
- *
- * \section selection_basics Basics
- *
- * Selections are enabled automatically for an analysis program that uses
- * the library. The selection syntax is described in an online help that is
- * accessible from all tools that use the library.
- * By default, dynamic selections are allowed, and the user can freely
- * choose whether to analyze atoms or centers of mass/geometry of
- * residues/molecules.
- * These defaults, as well as some others, can be changed by specifying
- * flags for gmx_ana_traj_create().
- *
- * The analysis program can then access the selected positions for each frame
- * through a \p gmx_ana_selection_t array that is passed to the frame
- * analysis function (see gmx_analysisfunc()).
- * As long as the analysis program is written such that it does not assume
- * that the number of positions or the atoms in the groups groups remain
- * constant, any kind of selection expression can be used.
- *
- * Some analysis programs may require a special structure for the index groups
- * (e.g., \c g_angle requires the index group to be made of groups of three or
- * four atoms).
- * For such programs, it is up to the user to provide a proper selection
- * expression that always returns such positions.
- * Such analysis program can define \ref ANA_REQUIRE_WHOLE to make the
- * default behavior appropriate for the most common uses where the groups
- * should consist of atoms within a single residue/molecule.
- *
- * \section selection_methods Implementing new keywords
- *
- * New selection keywords can be easily implemented, either directly into the
- * library or as part of analysis programs (the latter may be useful for
- * testing or methods very specific to some analysis).
- * For both cases, you should first create a \c gmx_ana_selmethod_t structure
- * and fill it with the necessary information.
- * Details can be found on a separate page: \ref selmethods.
- */
-/*! \internal \file
- * \brief Implementation of functions in trajana.h.
- */
-/*! \internal \dir src/gmxlib/trajana
- * \brief Source code for common trajectory analysis functions.
- *
- * Selection handling is found in \ref src/gmxlib/selection.
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <filenm.h>
-#include <futil.h>
-#include <macros.h>
-#include <pbc.h>
-#include <rmpbc.h>
-#include <smalloc.h>
-#include <statutil.h>
-#include <typedefs.h>
-#include <tpxio.h>
-#include <vec.h>
-
-#include <poscalc.h>
-#include <selection.h>
-#include <selmethod.h>
-#include <trajana.h>
-
-/*! \internal \brief
- * Data structure for trajectory analysis tools.
- */
-struct gmx_ana_traj_t
-{
- /*! \brief
- * Flags that alter the behavior of the analysis library.
- *
- * This variable stores the flags passed to gmx_ana_traj_create() for use
- * in the other functions.
- */
- unsigned long flags;
- /** Number of input reference groups. */
- int nrefgrps;
- /*! \brief
- * Number of input analysis groups.
- *
- * This is the number of groups in addition to the reference groups
- * that are required.
- * If -1, any number of groups (at least one) is acceptable.
- */
- int nanagrps;
- /*! \brief
- * Flags for init_first_frame() to specify what to read from the
- * trajectory.
- */
- int frflags;
- /** TRUE if molecules should be made whole for each frame. */
- gmx_bool bRmPBC;
- /*! \brief
- * TRUE if periodic boundary conditions should be used.
- *
- * If the flag is FALSE, the \p pbc pointer passed to gmx_analysisfunc()
- * is NULL.
- */
- gmx_bool bPBC;
-
- /** Name of the trajectory file (NULL if not provided). */
- char *trjfile;
- /** Name of the topology file (NULL if no topology loaded). */
- char *topfile;
- /** Non-NULL name of the topology file. */
- char *topfile_notnull;
- /** Name of the index file (NULL if no index file provided). */
- char *ndxfile;
- /** Name of the selection file (NULL if no file provided). */
- char *selfile;
- /** The selection string (NULL if not provided). */
- char *selection;
-
- /** The topology structure, or \p NULL if no topology loaded. */
- t_topology *top;
- /** TRUE if full tpx file was loaded, FALSE otherwise. */
- gmx_bool bTop;
- /** Coordinates from the topology (see \p bTopX). */
- rvec *xtop;
- /** The box loaded from the topology file. */
- matrix boxtop;
- /** The ePBC field loaded from the topology file. */
- int ePBC;
-
- /** The current frame, or \p NULL if no frame loaded yet. */
- t_trxframe *fr;
- /** Used to store the status variable from read_first_frame(). */
- t_trxstatus *status;
- /** The number of frames read. */
- int nframes;
-
- /** Number of elements in the \p sel array. */
- int ngrps;
- /*! \brief
- * Array of selection information (one element for each index group).
- *
- * After the call to gmx_ana_init_selections(), this array contains
- * information about the selections the user has provided.
- * The array contains \p ngrps elements;
- * if \p nanagrps was -1, the number may vary.
- * See \c gmx_ana_selection_t for details of the contents.
- *
- * The largest possible index groups for dynamic selections can be found
- * in \p sel[i]->g, i.e., the program can assume that any index group
- * passed to gmx_analysisfunc() is a subset of the provided group.
- * After gmx_ana_do(), the same groups can be used in post-processing.
- */
- gmx_ana_selection_t **sel;
- /** Array of names of the selections for convenience. */
- char **grpnames;
- /** Position calculation data. */
- gmx_ana_poscalc_coll_t *pcc;
- /** Selection data. */
- gmx_ana_selcollection_t *sc;
-
- /** Data for statutil.c utilities. */
- output_env_t oenv;
-};
-
-/** Loads the topology. */
-static int load_topology(gmx_ana_traj_t *d, gmx_bool bReq);
-/** Loads the first frame and does some checks. */
-static int init_first_frame(gmx_ana_traj_t *d);
-
-static int add_fnmarg(int nfile, t_filenm *fnm, t_filenm *fnm_add)
-{
- memcpy(&(fnm[nfile]), fnm_add, sizeof(*fnm_add));
- return nfile + 1;
-}
-
-/* Copied from src/gmxlib/statutil.c */
-static int
-add_parg(int npargs, t_pargs *pa, t_pargs *pa_add)
-{
- memcpy(&(pa[npargs]), pa_add, sizeof(*pa_add));
- return npargs + 1;
-}
-
-/*!
- * \param[out] data Trajectory analysis data structure poitner to initialize.
- * \param[in] flags Combination of flags (see \ref analysis_flags).
- * \returns 0 on success.
- */
-int
-gmx_ana_traj_create(gmx_ana_traj_t **data, unsigned long flags)
-{
- gmx_ana_traj_t *d;
- int rc;
-
- snew(d, 1);
-
- d->nrefgrps = 0;
- d->nanagrps = 1;
- d->frflags = TRX_NEED_X;
- d->bRmPBC = TRUE;
- d->bPBC = TRUE;
-
- d->trjfile = NULL;
- d->topfile = NULL;
- d->ndxfile = NULL;
- d->selfile = NULL;
- d->selection = NULL;
-
- d->top = NULL;
- d->bTop = FALSE;
- d->xtop = NULL;
- d->ePBC = -1;
- d->fr = NULL;
- d->nframes = 0;
-
- d->ngrps = 0;
- d->sel = NULL;
- d->grpnames = NULL;
-
- d->flags = flags;
- d->topfile_notnull = NULL;
- rc = gmx_ana_poscalc_coll_create(&d->pcc);
- if (rc != 0)
- {
- sfree(d);
- *data = NULL;
- return rc;
- }
- rc = gmx_ana_selcollection_create(&d->sc, d->pcc);
- if (rc != 0)
- {
- gmx_ana_poscalc_coll_free(d->pcc);
- sfree(d);
- *data = NULL;
- return rc;
- }
- d->status = NULL;
- d->oenv = NULL;
-
- *data = d;
- return 0;
-}
-
-/*!
- * \param d Trajectory analysis data to free.
- */
-void
-gmx_ana_traj_free(gmx_ana_traj_t *d)
-{
- int i;
-
- sfree(d->trjfile);
- sfree(d->topfile);
- sfree(d->topfile_notnull);
- sfree(d->ndxfile);
- sfree(d->selfile);
- if (d->top)
- {
- done_top(d->top);
- sfree(d->top);
- }
- if (d->fr)
- {
- /* Gromacs does not seem to have a function for freeing frame data */
- sfree(d->fr->x);
- sfree(d->fr->v);
- sfree(d->fr->f);
- sfree(d->fr);
- }
- sfree(d->xtop);
- sfree(d->sel);
- gmx_ana_selcollection_free(d->sc);
- gmx_ana_poscalc_coll_free(d->pcc);
- sfree(d->grpnames);
- output_env_done(d->oenv);
- sfree(d);
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] flags Additional flags to set.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Currently there are no checks whether the flags make sense or not.
- */
-int
-gmx_ana_add_flags(gmx_ana_traj_t *d, unsigned long flags)
-{
- d->flags |= flags;
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] bPBC TRUE if periodic boundary conditions should be used.
- * \returns 0 on success.
- *
- * If this function is called before parse_trjana_args(), it sets the default
- * for whether PBC are used in the analysis or not.
- * If \ref ANA_NOUSER_PBC is not set, a command-line option is provided for the
- * user to override the default value.
- * If called after parse_trjana_args(), it overrides the setting provided by
- * the user or an earlier call.
- *
- * If this function is not called, the default is to use PBC.
- *
- * If PBC are not used, the \p pbc pointer passed to gmx_analysisfunc()
- * is NULL.
- *
- * \see \ref ANA_NOUSER_PBC
- */
-int
-gmx_ana_set_pbc(gmx_ana_traj_t *d, gmx_bool bPBC)
-{
- d->bPBC = bPBC;
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \returns TRUE if periodic boundary conditions are set to be used.
- */
-gmx_bool
-gmx_ana_has_pbc(gmx_ana_traj_t *d)
-{
- return d->bPBC;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] bRmPBC TRUE if molecules should be made whole.
- * \returns 0 on success.
- *
- * If this function is called before parse_trjana_args(), it sets the default
- * for whether molecules are made whole.
- * If \ref ANA_NOUSER_RMPBC is not set, a command-line option is provided for
- * the user to override the default value.
- * If called after parse_trjana_args(), it overrides the setting provided by
- * the user or an earlier call.
- *
- * If this function is not called, the default is to make molecules whole.
- *
- * The main use of this function is to call it with FALSE if your analysis
- * program does not require whole molecules as this can increase the
- * performance.
- * In such a case, you can also specify \ref ANA_NOUSER_RMPBC to not to
- * confuse the user with an option that would only slow the program
- * down.
- *
- * \see \ref ANA_NOUSER_RMPBC
- */
-int
-gmx_ana_set_rmpbc(gmx_ana_traj_t *d, gmx_bool bRmPBC)
-{
- d->bRmPBC = bRmPBC;
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] frflags Flags for what to read from the trajectory file.
- * \returns 0 on success, an error code on error.
- *
- * The TRX_NEED_X flag is always set.
- * If the analysis tools needs some other information (velocities, forces),
- * it can call this function to load additional information from the
- * trajectory.
- */
-int
-gmx_ana_set_frflags(gmx_ana_traj_t *d, int frflags)
-{
- if (d->sel)
- {
- gmx_call("cannot set trajectory flags after initializing selections");
- return -1;
- }
- if (d->fr)
- {
- gmx_call("cannot set trajectory flags after the first frame has been read");
- return -1;
- }
- frflags |= TRX_NEED_X;
- d->frflags = frflags;
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] nrefgrps Number of reference groups required.
- * \returns 0 on success, a non-zero error code on error.
- *
- * \p nrefgrps should be a non-negative integer.
- * If this function is not called (or \p nrefgrps is 0), all selections are
- * treated as reference groups.
- */
-int
-gmx_ana_set_nrefgrps(gmx_ana_traj_t *d, int nrefgrps)
-{
- if (nrefgrps < 0)
- {
- d->nrefgrps = 0;
- gmx_incons("number of reference groups is negative");
- return EINVAL;
- }
- d->nrefgrps = nrefgrps;
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] nanagrps Number of analysis groups required
- * (-1 stands for any number of analysis groups).
- * \returns 0 on success, a non-zero error code on error.
- *
- * \p nanagrps should be a positive integer or -1.
- * In the latter case, any number of groups (but at least one) is acceptable.
- * gmx_ana_get_nanagrps() can be used to access the actual value after
- * gmx_ana_init_selections() has been called.
- * If this function is not called, a single analysis group is expected.
- */
-int
-gmx_ana_set_nanagrps(gmx_ana_traj_t *d, int nanagrps)
-{
- if (nanagrps <= 0 && nanagrps != -1)
- {
- d->nanagrps = 1;
- gmx_incons("number of analysis groups is invalid");
- return EINVAL;
- }
- d->nanagrps = nanagrps;
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[out] ngrps Total number of selections specified by the user.
- * \returns 0 on success, a non-zero error code on error.
- *
- * The value stored in \p *ngrps is the sum of the number of reference groups
- * and the number of analysis groups.
- * If a specific number (not -1) of analysis groups has been set with
- * gmx_ana_set_nanagrps(), the value is always the sum of the values provided
- * to gmx_ana_set_nrefgrps() and gmx_ana_set_nanagrps().
- *
- * Should only be called after gmx_ana_init_selections().
- */
-int
-gmx_ana_get_ngrps(gmx_ana_traj_t *d, int *ngrps)
-{
- if (d->nanagrps == -1)
- {
- *ngrps = 0;
- gmx_call("gmx_ana_init_selections() not called");
- return EINVAL;
- }
- *ngrps = d->nrefgrps + d->nanagrps;
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[out] nanagrps Number of analysis groups specified by the user.
- * \returns 0 on success, a non-zero error code on error.
- *
- * If a specific number (not -1) of analysis groups has been set with
- * gmx_ana_set_nanagrps(), the value is always the same value.
- * Hence, you only need to call this function if gmx_ana_set_nanagrps() has
- * been called with \p nanagrps set to -1.
- *
- * Should only be called after gmx_ana_init_selections().
- */
-int
-gmx_ana_get_nanagrps(gmx_ana_traj_t *d, int *nanagrps)
-{
- if (d->nanagrps == -1)
- {
- *nanagrps = 0;
- gmx_call("gmx_ana_init_selections() not called");
- return EINVAL;
- }
- *nanagrps = d->nanagrps;
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[in] i Ordinal number of the reference selection to get.
- * \param[out] sel Selection object for the \p i'th reference group.
- * \returns 0 on success, a non-zero error code on error.
- *
- * The pointer returned in \p *sel should not be freed.
- * Should only be called after gmx_ana_init_selections().
- */
-int
-gmx_ana_get_refsel(gmx_ana_traj_t *d, int i, gmx_ana_selection_t **sel)
-{
- if (i < 0 || i >= d->nrefgrps)
- {
- *sel = NULL;
- gmx_call("invalid reference group number");
- return EINVAL;
- }
- *sel = gmx_ana_selcollection_get_selection(d->sc, i);
- if (!*sel)
- {
- gmx_incons("gmx_ana_init_selections() not called");
- return EINVAL;
- }
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[out] sel Array of analysis selections.
- * \returns 0 on success, a non-zero error code on error.
- *
- * The pointer returned in \p *sel should not be freed.
- * Should only be called after gmx_ana_init_selections().
- */
-int
-gmx_ana_get_anagrps(gmx_ana_traj_t *d, gmx_ana_selection_t ***sel)
-{
- if (!d->sel)
- {
- *sel = NULL;
- gmx_incons("gmx_ana_init_selections() not called");
- return EINVAL;
- }
- *sel = d->sel;
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[out] grpnames Array of selection names.
- * \returns 0 on success, a non-zero error code on error.
- *
- * The pointer returned in \p *grpnames should not be freed.
- * Should only be called after gmx_ana_init_selections().
- */
-int
-gmx_ana_get_grpnames(gmx_ana_traj_t *d, char ***grpnames)
-{
- if (!d->grpnames)
- {
- *grpnames = NULL;
- gmx_call("gmx_ana_init_selections() not called");
- return EINVAL;
- }
- *grpnames = d->grpnames;
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[out] sc Selection collection object.
- * \returns 0 on success.
- *
- * This function is mostly useful for debugging purposes.
- * The information commonly required in analysis programs is accessible
- * more conveniently through other means.
- *
- * The pointer returned in \p *sc should not be freed.
- * Can be called at any point.
- */
-int
-gmx_ana_get_selcollection(gmx_ana_traj_t *d, gmx_ana_selcollection_t **sc)
-{
- *sc = d->sc;
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \returns 0 on success, a non-zero error code on error.
- *
- * This function should be called first in the analysis program, much in
- * the same way as parse_common_args() in traditional Gromacs analysis
- * programs. It adds some command-line arguments of its own, and uses
- * parse_common_args() to parse the command-line arguments.
- * It also loads topology information if required or if a topology is
- * provided on the command line.
- * Selection handling is also initialized if it is enabled and
- * the user has selected it on the command line.
- *
- * The rest of the parameters are passed on to the Gromacs routine
- * parse_common_args(), and the use of this function should be identical
- * to parse_common_args(), with the exception that for \p pca_flags,
- * \p PCA_CAN_TIME and \p PCA_BE_NICE flags are automatically added.
- * \param argc
- * \param argv
- * \param pca_flags
- * \param nfile
- * \param fnm
- * \param npargs
- * \param pa
- * \param ndesc
- * \param desc
- * \param nbugs
- * \param bugs
- * \param oenv
- */
-int
-parse_trjana_args(gmx_ana_traj_t *d,
- int *argc, char *argv[], unsigned long pca_flags,
- int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
- int ndesc, const char **desc,
- int nbugs, const char **bugs,
- output_env_t *oenv)
-{
- t_filenm *all_fnm = NULL;
- int max_fnm, nfall;
- int *fnm_map;
- t_pargs *all_pa = NULL;
- int max_pa, npall;
- size_t i;
- int k;
- int rc;
- const char *tmp_fnm;
-
- t_filenm def_fnm[] = {
- {efTRX, NULL, NULL, ffOPTRD},
- {efTPS, NULL, NULL, ffREAD},
- {efDAT, "-sf", "selection", ffOPTRD},
- {efNDX, NULL, NULL, ffOPTRD},
- };
- gmx_bool bPBC = TRUE;
- t_pargs pbc_pa[] = {
- {"-pbc", FALSE, etBOOL, {&bPBC},
- "Use periodic boundary conditions for distance calculation"},
- };
- gmx_bool bRmPBC = TRUE;
- t_pargs rmpbc_pa[] = {
- {"-rmpbc", FALSE, etBOOL, {&bRmPBC},
- "Make molecules whole for each frame"},
- };
- char *selection = NULL;
- const char **rpost = NULL;
- gmx_bool bSelDump = FALSE;
- t_pargs sel_pa[] = {
- {"-select", FALSE, etSTR, {&selection},
- "Selection string (use 'help' for help)"},
- {"-seldebug", FALSE, etBOOL, {&bSelDump},
- "HIDDENPrint out the parsed and compiled selection trees"},
- };
- t_pargs dsel_pa[] = {
- {"-selrpos", FALSE, etENUM, {NULL},
- "Selection reference position"},
- };
- const char **spost = NULL;
- t_pargs selpt_pa[] = {
- {"-seltype", FALSE, etENUM, {NULL},
- "Default analysis positions"},
- };
-#define MAX_PA asize(sel_pa)+asize(dsel_pa)+5
-
- if (d->nrefgrps < 0)
- {
- gmx_incons("number of reference groups is negative");
- return EINVAL;
- }
-
- if (d->flags & ANA_DEBUG_SELECTION)
- {
- bSelDump = TRUE;
- }
-
- rpost = gmx_ana_poscalc_create_type_enum(!(d->flags & ANA_REQUIRE_WHOLE));
- if (rpost == NULL)
- {
- return ENOMEM;
- }
- spost = gmx_ana_poscalc_create_type_enum(TRUE);
- if (spost == NULL)
- {
- sfree(rpost);
- return ENOMEM;
- }
-
- /* Construct the file name argument array */
- max_fnm = nfile + asize(def_fnm);
- snew(all_fnm, max_fnm);
- nfall = 0;
- if (!(d->flags & ANA_REQUIRE_TOP))
- {
- def_fnm[1].flag |= ffOPT;
- }
- snew(fnm_map, nfile);
- for (k = 0; k < nfile; ++k)
- {
- fnm_map[k] = -1;
- }
-
- for (i = 0; i < asize(def_fnm); ++i)
- {
- for (k = 0; k < nfile; ++k)
- {
- if (fnm_map[k] == -1 && def_fnm[i].opt == NULL
- && fnm[k].opt == NULL && fnm[k].ftp == def_fnm[i].ftp)
- {
- break;
- }
- }
- if (k < nfile)
- {
- fnm_map[k] = nfall;
- nfall = add_fnmarg(nfall, all_fnm, &(fnm[k]));
- }
- else
- {
- nfall = add_fnmarg(nfall, all_fnm, &(def_fnm[i]));
- }
- }
-
- for (k = 0; k < nfile; ++k)
- {
- if (fnm_map[k] == -1)
- {
- fnm_map[k] = nfall;
- nfall = add_fnmarg(nfall, all_fnm, &(fnm[k]));
- }
- }
-
- /* Construct the argument array */
- max_pa = npargs + MAX_PA;
- snew(all_pa, max_pa);
- npall = 0;
-
- if (!(d->flags & ANA_NOUSER_RMPBC))
- {
- for (i = 0; i < asize(rmpbc_pa); ++i)
- {
- npall = add_parg(npall, all_pa, &(rmpbc_pa[i]));
- }
- }
- if (!(d->flags & ANA_NOUSER_PBC))
- {
- for (i = 0; i < asize(pbc_pa); ++i)
- {
- npall = add_parg(npall, all_pa, &(pbc_pa[i]));
- }
- }
-
- for (i = 0; i < asize(sel_pa); ++i)
- {
- npall = add_parg(npall, all_pa, &(sel_pa[i]));
- }
- if (!(d->flags & ANA_NO_DYNSEL))
- {
- dsel_pa[0].u.c = rpost;
- for (i = 0; i < asize(dsel_pa); ++i)
- {
- npall = add_parg(npall, all_pa, &(dsel_pa[i]));
- }
- }
-
- if (!(d->flags & ANA_ONLY_ATOMPOS))
- {
- selpt_pa[0].u.c = spost;
- for (i = 0; i < asize(selpt_pa); ++i)
- {
- npall = add_parg(npall, all_pa, &(selpt_pa[i]));
- }
- }
-
- for (k = 0; k < npargs; ++k)
- {
- npall = add_parg(npall, all_pa, &(pa[k]));
- }
-
- pca_flags |= PCA_CAN_TIME | PCA_BE_NICE;
- parse_common_args(argc, argv, pca_flags, nfall, all_fnm, npall, all_pa,
- ndesc, desc, nbugs, bugs,oenv);
- d->oenv = *oenv;
-
- /* Process our own options.
- * Make copies of file names for easier memory management. */
- tmp_fnm = ftp2fn_null(efTRX, nfall, all_fnm);
- d->trjfile = tmp_fnm ? strdup(tmp_fnm) : NULL;
- tmp_fnm = ftp2fn_null(efTPS, nfall, all_fnm);
- d->topfile = tmp_fnm ? strdup(tmp_fnm) : NULL;
- d->topfile_notnull = strdup(ftp2fn(efTPS, nfall, all_fnm));
- tmp_fnm = ftp2fn_null(efNDX, nfall, all_fnm);
- d->ndxfile = tmp_fnm ? strdup(tmp_fnm) : NULL;
- if (!(d->flags & ANA_NOUSER_RMPBC))
- {
- d->bRmPBC = bRmPBC;
- }
- if (!(d->flags & ANA_NOUSER_PBC))
- {
- d->bPBC = bPBC;
- }
- d->selection = selection;
- tmp_fnm = opt2fn_null("-sf", nfall, all_fnm);
- d->selfile = tmp_fnm ? strdup(tmp_fnm) : NULL;
-
- /* Copy the results back */
- for (k = 0; k < nfile; ++k)
- {
- memcpy(&(fnm[k]), &(all_fnm[fnm_map[k]]), sizeof(fnm[k]));
- /* Delegate responsibility of freeing the file names to caller. */
- all_fnm[fnm_map[k]].nfiles = 0;
- all_fnm[fnm_map[k]].fns = NULL;
- }
- for (i = 0, k = npall - npargs; i < (size_t)npargs; ++i, ++k)
- {
- memcpy(&(pa[i]), &(all_pa[k]), sizeof(pa[i]));
- }
-
- /* Free memory we have allocated. */
- done_filenms(nfall, all_fnm);
- sfree(all_fnm);
- sfree(fnm_map);
- sfree(all_pa);
-
- if (!(d->flags & ANA_NO_DYNSEL))
- {
- gmx_ana_selcollection_set_refpostype(d->sc, rpost[0]);
- }
- else
- {
- gmx_ana_selcollection_set_refpostype(d->sc, rpost[1]);
- }
- sfree(rpost);
- if (bSelDump)
- {
- d->flags |= ANA_DEBUG_SELECTION;
- }
- else
- {
- d->flags &= ~ANA_DEBUG_SELECTION;
- }
-
- if (!(d->flags & ANA_ONLY_ATOMPOS))
- {
- gmx_ana_selcollection_set_outpostype(d->sc, spost[0], d->flags & ANA_USE_POSMASK);
- }
- else
- {
- gmx_ana_selcollection_set_outpostype(d->sc, spost[1], d->flags & ANA_USE_POSMASK);
- }
- sfree(spost);
-
- /* Check if the user requested help on selections.
- * If so, call gmx_ana_init_selections() to print the help and exit. */
- if (selection && strncmp(selection, "help", 4) == 0)
- {
- gmx_ana_init_selections(d);
- }
-
- /* If no trajectory file is given, we need to set some flags to be able
- * to prepare a frame from the loaded topology information. Also, check
- * that a topology is provided. */
- if (!d->trjfile)
- {
- if (!d->topfile)
- {
- gmx_input("No trajectory or topology provided, nothing to do!");
- return -1;
- }
- d->flags |= ANA_REQUIRE_TOP;
- d->flags |= ANA_USE_TOPX;
- }
-
- /* Load the topology if so requested. */
- rc = load_topology(d, (d->flags & ANA_REQUIRE_TOP));
- if (rc != 0)
- {
- return rc;
- }
-
- /* Initialize the selections/index groups */
- if (!(d->flags & ANA_USER_SELINIT))
- {
- rc = gmx_ana_init_selections(d);
- }
-
- return rc;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] bReq If TRUE, topology loading is forced.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Initializes the \c gmx_ana_traj_t::top, \c gmx_ana_traj_t::bTop,
- * \c gmx_ana_traj_t::boxtop and \c gmx_ana_traj_t::ePBC fields of the
- * analysis structure.
- * If \p bReq is TRUE, the topology is loaded even if it is not given on
- * the command line.
- *
- * The function can be called multiple times safely; extra calls are
- * ignored.
- */
-static int load_topology(gmx_ana_traj_t *d, gmx_bool bReq)
-{
- char title[STRLEN];
-
- /* Return if topology already loaded */
- if (d->top)
- {
- return 0;
- }
-
- if (d->topfile || bReq)
- {
- snew(d->top, 1);
- d->bTop = read_tps_conf(d->topfile_notnull, title, d->top,
- &d->ePBC, &d->xtop, NULL, d->boxtop, TRUE);
- if (!(d->flags & ANA_USE_TOPX))
- {
- sfree(d->xtop);
- d->xtop = NULL;
- }
- }
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[in] bReq If TRUE, topology loading is forced.
- * \param[out] top Topology data pointer to initialize.
- * \param[out] bTop TRUE if a full tpx file was loaded, FALSE otherwise
- * (can be NULL, in which case it is not used).
- * \returns 0 on success, a non-zero error code on error.
- *
- * If \ref ANA_REQUIRE_TOP has not been specified and \p bReq is FALSE,
- * the pointer stored in \p *top may be NULL if no topology has been provided
- * on the command line.
- *
- * The pointer returned in \p *top should not be freed.
- */
-int
-gmx_ana_get_topology(gmx_ana_traj_t *d, gmx_bool bReq, t_topology **top, gmx_bool *bTop)
-{
- int rc;
-
- rc = load_topology(d, bReq);
- if (rc != 0)
- {
- *top = NULL;
- return rc;
- }
- *top = d->top;
- if (bTop)
- {
- *bTop = d->bTop;
- }
- return 0;
-}
-
-/*!
- * \param[in] d Trajectory analysis data structure.
- * \param[out] x Topology data pointer to initialize.
- * (can be NULL, in which case it is not used).
- * \param[out] box Box size from the topology file
- * (can be NULL, in which case it is not used).
- * \param[out] ePBC The ePBC field from the topology
- * (can be NULL, in which case it is not used).
- * \returns 0 on success, a non-zero error code on error.
- *
- * If \ref ANA_USE_TOPX has not been specified, the \p x parameter should be
- * NULL.
- *
- * The pointer returned in \p *x should not be freed.
- */
-int
-gmx_ana_get_topconf(gmx_ana_traj_t *d, rvec **x, matrix box, int *ePBC)
-{
- if (box)
- {
- copy_mat(d->boxtop, box);
- }
- if (ePBC)
- {
- *ePBC = d->ePBC;
- }
- if (x)
- {
- if (!(d->flags & ANA_USE_TOPX))
- {
- gmx_incons("topology coordinates requested by ANA_USE_TOPX not set");
- *x = NULL;
- return EINVAL;
- }
- *x = d->xtop;
- }
- return 0;
-}
-
-/*! \brief
- * Loads default index groups from a selection file.
- *
- * \param[in,out] d Trajectory analysis data structure.
- * \param[out] grps Pointer to receive the default groups.
- * \returns 0 on success, a non-zero error code on error.
- */
-static int
-init_default_selections(gmx_ana_traj_t *d, gmx_ana_indexgrps_t **grps)
-{
- gmx_ana_selcollection_t *sc;
- char *fnm;
- int nr, nr_notempty, i;
- int rc;
-
- /* If an index file is provided, just load it and exit. */
- if (d->ndxfile)
- {
- gmx_ana_indexgrps_init(grps, d->top, d->ndxfile);
- return 0;
- }
- /* Initialize groups to NULL if we return prematurely. */
- *grps = NULL;
- /* Return immediately if no topology provided. */
- if (!d->top)
- {
- return 0;
- }
-
- /* Find the default selection file, return if none found. */
- fnm = low_gmxlibfn("defselection.dat", TRUE, FALSE);
- if (fnm == NULL)
- {
- return 0;
- }
-
- /* Create a temporary selection collection. */
- rc = gmx_ana_selcollection_create(&sc, d->pcc);
- if (rc != 0)
- {
- sfree(fnm);
- return rc;
- }
- rc = gmx_ana_selmethod_register_defaults(sc);
- if (rc != 0)
- {
- gmx_ana_selcollection_free(sc);
- sfree(fnm);
- gmx_fatal(FARGS, "default selection method registration failed");
- return rc;
- }
- /* FIXME: It would be better to not have the strings here hard-coded. */
- gmx_ana_selcollection_set_refpostype(sc, "atom");
- gmx_ana_selcollection_set_outpostype(sc, "atom", FALSE);
-
- /* Parse and compile the file with no external groups. */
- rc = gmx_ana_selcollection_parse_file(sc, fnm, NULL);
- sfree(fnm);
- if (rc != 0)
- {
- gmx_ana_selcollection_free(sc);
- fprintf(stderr, "\nWARNING: default selection(s) could not be parsed\n");
- return rc;
- }
- gmx_ana_selcollection_set_topology(sc, d->top, -1);
- rc = gmx_ana_selcollection_compile(sc);
- if (rc != 0)
- {
- gmx_ana_selcollection_free(sc);
- fprintf(stderr, "\nWARNING: default selection(s) could not be compiled\n");
- return rc;
- }
-
- /* Count the non-empty groups and check that there are no dynamic
- * selections. */
- nr = gmx_ana_selcollection_get_count(sc);
- nr_notempty = 0;
- for (i = 0; i < nr; ++i)
- {
- gmx_ana_selection_t *sel;
-
- sel = gmx_ana_selcollection_get_selection(sc, i);
- if (sel->bDynamic)
- {
- fprintf(stderr, "\nWARNING: dynamic default selection ignored\n");
- }
- else if (sel->g->isize > 0)
- {
- ++nr_notempty;
- }
- }
-
- /* Copy the groups to the output structure */
- gmx_ana_indexgrps_alloc(grps, nr_notempty);
- nr_notempty = 0;
- for (i = 0; i < nr; ++i)
- {
- gmx_ana_selection_t *sel;
-
- sel = gmx_ana_selcollection_get_selection(sc, i);
- if (!sel->bDynamic && sel->g->isize > 0)
- {
- gmx_ana_index_t *g;
-
- g = gmx_ana_indexgrps_get_grp(*grps, nr_notempty);
- gmx_ana_index_copy(g, sel->g, TRUE);
- g->name = strdup(sel->name);
- ++nr_notempty;
- }
- }
-
- gmx_ana_selcollection_free(sc);
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \returns 0 on success, a non-zero error code on error.
- *
- * Initializes the selection data in \c gmx_ana_traj_t based on
- * the selection options and/or index files provided on the command line.
- *
- * This function is called automatically by parse_trjana_args() and should
- * not be called directly unless \ref ANA_USER_SELINIT is specified.
- *
- * \see ANA_USER_SELINIT
- */
-int
-gmx_ana_init_selections(gmx_ana_traj_t *d)
-{
- int rc;
- int i;
- int nr;
- gmx_ana_indexgrps_t *grps;
- int natoms;
- gmx_bool bStdIn;
- gmx_bool bInteractive;
- gmx_bool bOk;
-
- if (d->sel)
- {
- gmx_call("init_selections called more than once\n"
- "perhaps you forgot ANA_USER_SELINIT");
- return -1;
- }
-
- gmx_ana_selcollection_set_veloutput(d->sc,
- d->frflags & (TRX_READ_V | TRX_NEED_V));
- gmx_ana_selcollection_set_forceoutput(d->sc,
- d->frflags & (TRX_READ_F | TRX_NEED_F));
- /* Check if we need some information from the topology */
- if (gmx_ana_selcollection_requires_top(d->sc))
- {
- rc = load_topology(d, TRUE);
- if (rc != 0)
- {
- return rc;
- }
- }
- /* Initialize the default selection methods */
- rc = gmx_ana_selmethod_register_defaults(d->sc);
- if (rc != 0)
- {
- gmx_fatal(FARGS, "default selection method registration failed");
- return rc;
- }
- /* Initialize index groups.
- * We ignore the return value to continue without the default groups if
- * there is an error there. */
- init_default_selections(d, &grps);
- /* Parse the selections */
- bStdIn = (d->selfile && d->selfile[0] == '-' && d->selfile[1] == 0)
- || (d->selection && d->selection[0] == 0)
- || (!d->selfile && !d->selection);
- /* Behavior is not very pretty if we cannot check for interactive input,
- * but at least it should compile and work in most cases. */
-#ifdef HAVE_UNISTD_H
- bInteractive = bStdIn && isatty(fileno(stdin));
-#else
- bInteractive = bStdIn;
-#endif
- if (bStdIn && bInteractive)
- {
- /* Parse from stdin */
- /* First we parse the reference groups if there are any */
- if (d->nrefgrps > 0)
- {
- fprintf(stderr, "\nSpecify ");
- if (d->nrefgrps == 1)
- {
- fprintf(stderr, "a reference selection");
- }
- else
- {
- fprintf(stderr, "%d reference selections", d->nrefgrps);
- }
- fprintf(stderr, ":\n");
- fprintf(stderr, "(one selection per line, 'help' for help)\n");
- rc = gmx_ana_selcollection_parse_stdin(d->sc, d->nrefgrps, grps, TRUE);
- nr = gmx_ana_selcollection_get_count(d->sc);
- if (rc != 0 || nr != d->nrefgrps)
- {
- gmx_ana_traj_free(d);
- gmx_input("unrecoverable error in selection parsing");
- return rc;
- }
- }
- /* Then, we parse the analysis groups */
- fprintf(stderr, "\nSpecify ");
- if (d->nanagrps == 1)
- {
- fprintf(stderr, "a selection");
- }
- else if (d->nanagrps == -1)
- {
- fprintf(stderr, "any number of selections");
- }
- else
- {
- fprintf(stderr, "%d selections", d->nanagrps);
- }
- fprintf(stderr, " for analysis:\n");
- fprintf(stderr, "(one selection per line, 'help' for help%s)\n",
- d->nanagrps == -1 ? ", Ctrl-D to end" : "");
- rc = gmx_ana_selcollection_parse_stdin(d->sc, d->nanagrps, grps, TRUE);
- fprintf(stderr, "\n");
- }
- else if (bStdIn)
- {
- rc = gmx_ana_selcollection_parse_stdin(d->sc, -1, grps, FALSE);
- }
- else if (d->selection)
- {
- rc = gmx_ana_selcollection_parse_str(d->sc, d->selection, grps);
- }
- else
- {
- rc = gmx_ana_selcollection_parse_file(d->sc, d->selfile, grps);
- }
- if (grps)
- {
- gmx_ana_indexgrps_free(grps);
- }
- if (rc != 0)
- {
- /* Free memory for memory leak checking */
- gmx_ana_traj_free(d);
- gmx_input("selection(s) could not be parsed");
- return rc;
- }
-
- /* Check the number of groups */
- nr = gmx_ana_selcollection_get_count(d->sc);
- if (nr == 0)
- {
- /* TODO: Don't print this if the user has requested help */
- fprintf(stderr, "Nothing selected, finishing up.\n");
- gmx_ana_traj_free(d);
- /* TODO: It would be better to return some code that tells the caller
- * that one should exit. */
- exit(0);
- }
- if (nr <= d->nrefgrps)
- {
- gmx_input("selection does not specify enough index groups");
- return -1;
- }
- if (d->nanagrps <= 0)
- {
- d->nanagrps = nr - d->nrefgrps;
- }
- else if (nr != d->nrefgrps + d->nanagrps)
- {
- gmx_input("selection does not specify the correct number of index groups");
- return -1;
- }
-
- if (d->flags & ANA_DEBUG_SELECTION)
- {
- gmx_ana_selcollection_print_tree(stderr, d->sc, FALSE);
- }
- if (gmx_ana_selcollection_requires_top(d->sc))
- {
- rc = load_topology(d, TRUE);
- if (rc != 0)
- {
- return rc;
- }
- }
- if (d->top)
- {
- natoms = -1;
- }
- else
- {
- rc = init_first_frame(d);
- if (rc != 0)
- {
- return rc;
- }
- natoms = d->fr->natoms;
- }
- gmx_ana_selcollection_set_topology(d->sc, d->top, natoms);
- rc = gmx_ana_selcollection_compile(d->sc);
- if (rc != 0)
- {
- /* Free memory for memory leak checking */
- gmx_ana_traj_free(d);
- gmx_input("selection could not be compiled");
- return rc;
- }
- /* Create the selection array */
- d->ngrps = gmx_ana_selcollection_get_count(d->sc);
- if (!(d->flags & ANA_USE_FULLGRPS))
- {
- d->ngrps -= d->nrefgrps;
- }
- snew(d->sel, d->ngrps);
- for (i = 0; i < d->ngrps; ++i)
- {
- if (d->flags & ANA_USE_FULLGRPS)
- {
- d->sel[i] = gmx_ana_selcollection_get_selection(d->sc, i);
- }
- else
- {
- d->sel[i] = gmx_ana_selcollection_get_selection(d->sc, i + d->nrefgrps);
- }
- }
- if (d->flags & ANA_DEBUG_SELECTION)
- {
- fprintf(stderr, "\n");
- gmx_ana_selcollection_print_tree(stderr, d->sc, FALSE);
- fprintf(stderr, "\n");
- gmx_ana_poscalc_coll_print_tree(stderr, d->pcc);
- fprintf(stderr, "\n");
- }
-
- /* Initialize the position evaluation */
- gmx_ana_poscalc_init_eval(d->pcc);
- if (d->flags & ANA_DEBUG_SELECTION)
- {
- gmx_ana_poscalc_coll_print_tree(stderr, d->pcc);
- fprintf(stderr, "\n");
- }
-
- /* Check that dynamic selections are not provided if not allowed */
- if (d->flags & ANA_NO_DYNSEL)
- {
- for (i = 0; i < d->nrefgrps + d->nanagrps; ++i)
- {
- gmx_ana_selection_t *sel;
-
- sel = gmx_ana_selcollection_get_selection(d->sc, i);
- if (sel->bDynamic)
- {
- gmx_fatal(FARGS, "%s does not support dynamic selections",
- ShortProgram());
- return -1;
- }
- }
- }
- /* Check that non-atom positions are not provided if not allowed.
- * TODO: It would be better to have these checks in the parser. */
- if (d->flags & ANA_ONLY_ATOMPOS)
- {
- for (i = 0; i < d->nanagrps; ++i)
- {
- gmx_ana_selection_t *sel;
-
- sel = gmx_ana_selcollection_get_selection(d->sc, i + d->nrefgrps);
- if (sel->p.m.type != INDEX_ATOM)
- {
- gmx_fatal(FARGS, "%s does not support non-atom positions",
- ShortProgram());
- return -1;
- }
- }
- }
- /* Create the names array */
- snew(d->grpnames, d->ngrps);
- for (i = 0; i < d->ngrps; ++i)
- {
- d->grpnames[i] = gmx_ana_selection_name(d->sel[i]);
- }
-
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] type Type of covered fractions to calculate.
- * \returns 0 on success, a non-zero error code on error.
- *
- * By default, covered fractions are not calculated.
- * If this function is called, the covered fraction calculation is
- * initialize to calculate the fractions of type \p type for each selection.
- * The function must be called after gmx_ana_init_selections() has been
- * called.
- *
- * For more fine-grained control of the calculation, you can use
- * gmx_ana_selection_init_coverfrac(): if you initialize some selections
- * this function to something else than CFRAC_NONE before calling
- * gmx_ana_init_coverfrac(), these settings are not overwritten.
- *
- * You only need to call this function if your program needs to have
- * information about the covered fractions, e.g., for normalization.
- *
- * \see gmx_ana_selection_init_coverfrac()
- */
-int
-gmx_ana_init_coverfrac(gmx_ana_traj_t *d, e_coverfrac_t type)
-{
- int g;
-
- if (type == CFRAC_NONE)
- {
- return 0;
- }
-
- for (g = 0; g < d->ngrps; ++g)
- {
- if (d->sel[g]->cfractype == CFRAC_NONE)
- {
- gmx_ana_selection_init_coverfrac(d->sel[g], type);
- }
- }
- return 0;
-}
-
-/*!
- * \param[in] out Output file.
- * \param[in] d Trajectory analysis data structure.
- * \returns 0 on success, a non-zero error code on error.
- */
-int xvgr_selections(FILE *out, gmx_ana_traj_t *d)
-{
- xvgr_selcollection(out, d->sc, d->oenv);
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \returns 0 on success, a non-zero error code on error.
- */
-static int init_first_frame(gmx_ana_traj_t *d)
-{
- int i;
-
- /* Return if we have already initialized the trajectory */
- if (d->fr)
- {
- return 0;
- }
-
- d->frflags |= TRX_NEED_X;
-
- snew(d->fr, 1);
-
- if (d->trjfile)
- {
- if (!read_first_frame(d->oenv, &d->status, d->trjfile, d->fr, d->frflags))
- {
- gmx_input("could not read coordinates from trajectory");
- return EIO;
- }
-
- if (d->top && d->fr->natoms > d->top->atoms.nr)
- {
- gmx_fatal(FARGS, "Trajectory (%d atoms) does not match topology (%d atoms)",
- d->fr->natoms, d->top->atoms.nr);
- return -1;
- }
- /* check index groups */
- for (i = 0; i < d->ngrps; ++i)
- {
- gmx_ana_index_check(d->sel[i]->g, d->fr->natoms);
- }
- }
- else
- {
- /* Prepare a frame from topology information */
- /* TODO: Initialize more of the fields */
- if (d->frflags & (TRX_NEED_V))
- {
- gmx_impl("Velocity reading from a topology not implemented");
- return -1;
- }
- if (d->frflags & (TRX_NEED_F))
- {
- gmx_input("Forces cannot be read from a topology");
- return -1;
- }
- d->fr->flags = d->frflags;
- d->fr->natoms = d->top->atoms.nr;
- d->fr->bX = TRUE;
- snew(d->fr->x, d->fr->natoms);
- memcpy(d->fr->x, d->xtop, sizeof(*d->fr->x)*d->fr->natoms);
- d->fr->bBox = TRUE;
- copy_mat(d->boxtop, d->fr->box);
- }
-
- set_trxframe_ePBC(d->fr, d->ePBC);
-
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[out] fr First frame in the trajectory.
- * \returns 0 on success, a non-zero error code on error.
- *
- * The pointer stored in \p *fr should not be freed by the caller.
- *
- * You only need to call this function if your program needs to do some
- * initialization for which it requires data from the first frame.
- *
- * \see gmx_ana_do()
- */
-int gmx_ana_get_first_frame(gmx_ana_traj_t *d, t_trxframe **fr)
-{
- int rc;
-
- rc = init_first_frame(d);
- if (rc != 0)
- {
- *fr = NULL;
- return rc;
- }
- *fr = d->fr;
- return 0;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[in] flags Combination of flags
- * (currently, there are no flags defined).
- * \param[in] analyze Pointer to frame analysis function.
- * \param data User data to be passed to \p analyze.
- * \returns 0 on success, a non-zero error code on error.
- *
- * This function performs the actual analysis of the trajectory.
- * It loops through all the frames in the trajectory, and calls
- * \p analyze for each frame to perform the actual analysis.
- * Before calling \p analyze, selections are updated (if there are any).
- * See gmx_analysisfunc() for description of \p analyze parameters.
- *
- * This function also calculates the number of frames during the run.
- */
-int gmx_ana_do(gmx_ana_traj_t *d, int flags, gmx_analysisfunc analyze, void *data)
-{
- t_pbc pbc;
- t_pbc *ppbc;
- int rc;
- gmx_rmpbc_t gpbc=NULL;
-
- rc = init_first_frame(d);
- if (rc != 0)
- {
- return rc;
- }
-
- ppbc = d->bPBC ? &pbc : 0;
- if (!d->top)
- {
- d->bRmPBC = FALSE;
- }
- if (d->bRmPBC)
- {
- gpbc = gmx_rmpbc_init(&d->top->idef,d->ePBC,d->fr->natoms,d->fr->box);
- }
- d->nframes = 0;
- do
- {
- if (d->bRmPBC)
- {
- gmx_rmpbc_trxfr(gpbc,d->fr);
- }
- if (ppbc)
- {
- set_pbc(&pbc, d->ePBC, d->fr->box);
- }
-
- gmx_ana_poscalc_init_frame(d->pcc);
- rc = gmx_ana_selcollection_evaluate(d->sc, d->fr, ppbc);
- if (rc != 0)
- {
- close_trj(d->status);
- gmx_fatal(FARGS, "selection evaluation failed");
- return rc;
- }
- rc = analyze(d->top, d->fr, ppbc, d->ngrps, d->sel, data);
- if (rc != 0)
- {
- close_trj(d->status);
- return rc;
- }
-
- d->nframes++;
- }
- while (d->trjfile && read_next_frame(d->oenv, d->status, d->fr));
- if (d->bRmPBC)
- {
- gmx_rmpbc_done(gpbc);
- }
- if (d->trjfile)
- {
- close_trj(d->status);
- fprintf(stderr, "Analyzed %d frames, last time %.3f\n",
- d->nframes, d->fr->time);
- }
- else
- {
- fprintf(stderr, "Analyzed topology coordinates\n");
- }
-
- /* Restore the maximal groups for dynamic selections */
- rc = gmx_ana_selcollection_evaluate_fin(d->sc, d->nframes);
- if (rc != 0)
- {
- gmx_fatal(FARGS, "selection evaluation failed");
- }
-
- return rc;
-}
-
-/*!
- * \param[in,out] d Trajectory analysis data structure.
- * \param[out] nframes Number of frames.
- * \returns 0 on success, a non-zero error code on error.
- *
- * If called before gmx_ana_do(), the behavior is currently undefined.
- */
-extern int
-gmx_ana_get_nframes(gmx_ana_traj_t *d, int *nframes)
-{
- *nframes = d->nframes;
- return 0;
-}
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROningen Mixture of Alchemy and Childrens' Stories
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-/* This file is completely threadsafe - please keep it that way! */
-#ifdef GMX_THREADS
-#include <thread_mpi.h>
-#endif
-
-
-#include <stdio.h>
-#include "smalloc.h"
-#include "typedefs.h"
-#include "names.h"
-#include "txtdump.h"
-#include "string2.h"
-#include "vec.h"
-
-
-int pr_indent(FILE *fp,int n)
-{
- int i;
-
- for (i=0; i<n; i++) (void) fprintf(fp," ");
- return n;
-}
-
-int available(FILE *fp,void *p,int indent,const char *title)
-{
- if (!p) {
- if (indent > 0)
- pr_indent(fp,indent);
- (void) fprintf(fp,"%s: not available\n",title);
- }
- return (p!=NULL);
-}
-
-int pr_title(FILE *fp,int indent,const char *title)
-{
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s:\n",title);
- return (indent+INDENT);
-}
-
-int pr_title_n(FILE *fp,int indent,const char *title,int n)
-{
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s (%d):\n",title,n);
- return (indent+INDENT);
-}
-
-int pr_title_nxn(FILE *fp,int indent,const char *title,int n1,int n2)
-{
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s (%dx%d):\n",title,n1,n2);
- return (indent+INDENT);
-}
-
-void pr_ivec(FILE *fp,int indent,const char *title,int vec[],int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,vec,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]=%d\n",title,bShowNumbers?i:-1,vec[i]);
- }
- }
-}
-
-void pr_ivec_block(FILE *fp,int indent,const char *title,int vec[],int n, gmx_bool bShowNumbers)
-{
- int i,j;
-
- if (available(fp,vec,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- i = 0;
- while (i < n)
- {
- j = i+1;
- while (j < n && vec[j] == vec[j-1]+1)
- {
- j++;
- }
- /* Print consecutive groups of 3 or more as blocks */
- if (j - i < 3)
- {
- while(i < j)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]=%d\n",
- title,bShowNumbers?i:-1,vec[i]);
- i++;
- }
- }
- else
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d,...,%d] = {%d,...,%d}\n",
- title,
- bShowNumbers?i:-1,
- bShowNumbers?j-1:-1,
- vec[i],vec[j-1]);
- i = j;
- }
- }
- }
-}
-
-void pr_bvec(FILE *fp,int indent,const char *title,gmx_bool vec[],int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,vec,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]=%s\n",title,bShowNumbers?i:-1,
- BOOL(vec[i]));
- }
- }
-}
-
-void pr_ivecs(FILE *fp,int indent,const char *title,ivec vec[],int n, gmx_bool bShowNumbers)
-{
- int i,j;
-
- if (available(fp,vec,indent,title))
- {
- indent=pr_title_nxn(fp,indent,title,n,DIM);
- for (i=0; i<n; i++)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]={",title,bShowNumbers?i:-1);
- for (j=0; j<DIM; j++)
- {
- if (j!=0) (void) fprintf(fp,", ");
- fprintf(fp,"%d",vec[i][j]);
- }
- (void) fprintf(fp,"}\n");
- }
- }
-}
-
-void pr_rvec(FILE *fp,int indent,const char *title,real vec[],int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,vec,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- pr_indent(fp,indent);
- fprintf(fp,"%s[%d]=%12.5e\n",title,bShowNumbers?i:-1,vec[i]);
- }
- }
-}
-
-void pr_dvec(FILE *fp,int indent,const char *title,double vec[],int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,vec,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- pr_indent(fp,indent);
- fprintf(fp,"%s[%d]=%12.5e\n",title,bShowNumbers?i:-1,vec[i]);
- }
- }
-}
-
-
-/*
-void pr_mat(FILE *fp,int indent,char *title,matrix m)
-{
- int i,j;
-
- if (available(fp,m,indent,title)) {
- indent=pr_title_n(fp,indent,title,n);
- for(i=0; i<n; i++) {
- pr_indent(fp,indent);
- fprintf(fp,"%s[%d]=%12.5e %12.5e %12.5e\n",
- title,bShowNumbers?i:-1,m[i][XX],m[i][YY],m[i][ZZ]);
- }
- }
-}
-*/
-
-void pr_rvecs_len(FILE *fp,int indent,const char *title,rvec vec[],int n)
-{
- int i,j;
-
- if (available(fp,vec,indent,title)) {
- indent=pr_title_nxn(fp,indent,title,n,DIM);
- for (i=0; i<n; i++) {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%5d]={",title,i);
- for (j=0; j<DIM; j++) {
- if (j != 0)
- (void) fprintf(fp,", ");
- (void) fprintf(fp,"%12.5e",vec[i][j]);
- }
- (void) fprintf(fp,"} len=%12.5e\n",norm(vec[i]));
- }
- }
-}
-
-void pr_rvecs(FILE *fp,int indent,const char *title,rvec vec[],int n)
-{
- const char *fshort = "%12.5e";
- const char *flong = "%15.8e";
- const char *format;
- int i,j;
-
- if (getenv("LONGFORMAT") != NULL)
- format = flong;
- else
- format = fshort;
-
- if (available(fp,vec,indent,title)) {
- indent=pr_title_nxn(fp,indent,title,n,DIM);
- for (i=0; i<n; i++) {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%5d]={",title,i);
- for (j=0; j<DIM; j++) {
- if (j != 0)
- (void) fprintf(fp,", ");
- (void) fprintf(fp,format,vec[i][j]);
- }
- (void) fprintf(fp,"}\n");
- }
- }
-}
-
-
-void pr_reals(FILE *fp,int indent,const char *title,real *vec,int n)
-{
- int i;
-
- if (available(fp,vec,indent,title)) {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s:\t",title);
- for(i=0; i<n; i++)
- fprintf(fp," %10g",vec[i]);
- (void) fprintf(fp,"\n");
- }
-}
-
-void pr_doubles(FILE *fp,int indent,const char *title,double *vec,int n)
-{
- int i;
-
- if (available(fp,vec,indent,title)) {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s:\t",title);
- for(i=0; i<n; i++)
- fprintf(fp," %10g",vec[i]);
- (void) fprintf(fp,"\n");
- }
-}
-
-static void pr_int(FILE *fp,int indent,const char *title,int i)
-{
- pr_indent(fp,indent);
- fprintf(fp,"%-20s = %d\n",title,i);
-}
-
-static void pr_gmx_large_int(FILE *fp,int indent,const char *title,gmx_large_int_t i)
-{
- char buf[STEPSTRSIZE];
-
- pr_indent(fp,indent);
- fprintf(fp,"%-20s = %s\n",title,gmx_step_str(i,buf));
-}
-
-static void pr_real(FILE *fp,int indent,const char *title,real r)
-{
- pr_indent(fp,indent);
- fprintf(fp,"%-20s = %g\n",title,r);
-}
-
-static void pr_double(FILE *fp,int indent,const char *title,double d)
-{
- pr_indent(fp,indent);
- fprintf(fp,"%-20s = %g\n",title,d);
-}
-
-static void pr_str(FILE *fp,int indent,const char *title,const char *s)
-{
- pr_indent(fp,indent);
- fprintf(fp,"%-20s = %s\n",title,s);
-}
-
-void pr_qm_opts(FILE *fp,int indent,const char *title,t_grpopts *opts)
-{
- int i,m,j;
-
- fprintf(fp,"%s:\n",title);
-
- pr_int(fp,indent,"ngQM",opts->ngQM);
- if (opts->ngQM > 0) {
- pr_ivec(fp,indent,"QMmethod",opts->QMmethod,opts->ngQM,FALSE);
- pr_ivec(fp,indent,"QMbasis",opts->QMbasis,opts->ngQM,FALSE);
- pr_ivec(fp,indent,"QMcharge",opts->QMcharge,opts->ngQM,FALSE);
- pr_ivec(fp,indent,"QMmult",opts->QMmult,opts->ngQM,FALSE);
- pr_bvec(fp,indent,"bSH",opts->bSH,opts->ngQM,FALSE);
- pr_ivec(fp,indent,"CASorbitals",opts->CASorbitals,opts->ngQM,FALSE);
- pr_ivec(fp,indent,"CASelectrons",opts->CASelectrons,opts->ngQM,FALSE);
- pr_rvec(fp,indent,"SAon",opts->SAon,opts->ngQM,FALSE);
- pr_rvec(fp,indent,"SAon",opts->SAon,opts->ngQM,FALSE);
- pr_ivec(fp,indent,"SAsteps",opts->SAsteps,opts->ngQM,FALSE);
- pr_bvec(fp,indent,"bOPT",opts->bOPT,opts->ngQM,FALSE);
- pr_bvec(fp,indent,"bTS",opts->bTS,opts->ngQM,FALSE);
- }
-}
-
-static void pr_grp_opts(FILE *out,int indent,const char *title,t_grpopts *opts,
- gmx_bool bMDPformat)
-{
- int i,m,j;
-
- if (!bMDPformat)
- fprintf(out,"%s:\n",title);
-
- pr_indent(out,indent);
- fprintf(out,"nrdf%s",bMDPformat ? " = " : ":");
- for(i=0; (i<opts->ngtc); i++)
- fprintf(out," %10g",opts->nrdf[i]);
- fprintf(out,"\n");
-
- pr_indent(out,indent);
- fprintf(out,"ref_t%s",bMDPformat ? " = " : ":");
- for(i=0; (i<opts->ngtc); i++)
- fprintf(out," %10g",opts->ref_t[i]);
- fprintf(out,"\n");
-
- pr_indent(out,indent);
- fprintf(out,"tau_t%s",bMDPformat ? " = " : ":");
- for(i=0; (i<opts->ngtc); i++)
- fprintf(out," %10g",opts->tau_t[i]);
- fprintf(out,"\n");
-
- /* Pretty-print the simulated annealing info */
- fprintf(out,"anneal%s",bMDPformat ? " = " : ":");
- for(i=0; (i<opts->ngtc); i++)
- fprintf(out," %10s",EANNEAL(opts->annealing[i]));
- fprintf(out,"\n");
-
- fprintf(out,"ann_npoints%s",bMDPformat ? " = " : ":");
- for(i=0; (i<opts->ngtc); i++)
- fprintf(out," %10d",opts->anneal_npoints[i]);
- fprintf(out,"\n");
-
- for(i=0; (i<opts->ngtc); i++) {
- if(opts->anneal_npoints[i]>0) {
- fprintf(out,"ann. times [%d]:\t",i);
- for(j=0; (j<opts->anneal_npoints[i]); j++)
- fprintf(out," %10.1f",opts->anneal_time[i][j]);
- fprintf(out,"\n");
- fprintf(out,"ann. temps [%d]:\t",i);
- for(j=0; (j<opts->anneal_npoints[i]); j++)
- fprintf(out," %10.1f",opts->anneal_temp[i][j]);
- fprintf(out,"\n");
- }
- }
-
- pr_indent(out,indent);
- fprintf(out,"acc:\t");
- for(i=0; (i<opts->ngacc); i++)
- for(m=0; (m<DIM); m++)
- fprintf(out," %10g",opts->acc[i][m]);
- fprintf(out,"\n");
-
- pr_indent(out,indent);
- fprintf(out,"nfreeze:");
- for(i=0; (i<opts->ngfrz); i++)
- for(m=0; (m<DIM); m++)
- fprintf(out," %10s",opts->nFreeze[i][m] ? "Y" : "N");
- fprintf(out,"\n");
-
-
- for(i=0; (i<opts->ngener); i++) {
- pr_indent(out,indent);
- fprintf(out,"energygrp_flags[%3d]:",i);
- for(m=0; (m<opts->ngener); m++)
- fprintf(out," %d",opts->egp_flags[opts->ngener*i+m]);
- fprintf(out,"\n");
- }
-
- fflush(out);
-}
-
-static void pr_matrix(FILE *fp,int indent,const char *title,rvec *m,
- gmx_bool bMDPformat)
-{
- if (bMDPformat)
- fprintf(fp,"%-10s = %g %g %g %g %g %g\n",title,
- m[XX][XX],m[YY][YY],m[ZZ][ZZ],m[XX][YY],m[XX][ZZ],m[YY][ZZ]);
- else
- pr_rvecs(fp,indent,title,m,DIM);
-}
-
-static void pr_cosine(FILE *fp,int indent,const char *title,t_cosines *cos,
- gmx_bool bMDPformat)
-{
- int j;
-
- if (bMDPformat) {
- fprintf(fp,"%s = %d\n",title,cos->n);
- }
- else {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- fprintf(fp,"n = %d\n",cos->n);
- if (cos->n > 0) {
- (void) pr_indent(fp,indent+2);
- fprintf(fp,"a =");
- for(j=0; (j<cos->n); j++)
- fprintf(fp," %e",cos->a[j]);
- fprintf(fp,"\n");
- (void) pr_indent(fp,indent+2);
- fprintf(fp,"phi =");
- for(j=0; (j<cos->n); j++)
- fprintf(fp," %e",cos->phi[j]);
- fprintf(fp,"\n");
- }
- }
-}
-
-#define PS(t,s) pr_str(fp,indent,t,s)
-#define PI(t,s) pr_int(fp,indent,t,s)
-#define PSTEP(t,s) pr_gmx_large_int(fp,indent,t,s)
-#define PR(t,s) pr_real(fp,indent,t,s)
-#define PD(t,s) pr_double(fp,indent,t,s)
-
-static void pr_pullgrp(FILE *fp,int indent,int g,t_pullgrp *pg)
-{
- pr_indent(fp,indent);
- fprintf(fp,"pull_group %d:\n",g);
- indent += 2;
- pr_ivec_block(fp,indent,"atom",pg->ind,pg->nat,TRUE);
- pr_rvec(fp,indent,"weight",pg->weight,pg->nweight,TRUE);
- PI("pbcatom",pg->pbcatom);
- pr_rvec(fp,indent,"vec",pg->vec,DIM,TRUE);
- pr_rvec(fp,indent,"init",pg->init,DIM,TRUE);
- PR("rate",pg->rate);
- PR("k",pg->k);
- PR("kB",pg->kB);
-}
-
-static void pr_pull(FILE *fp,int indent,t_pull *pull)
-{
- int g;
-
- PS("pull_geometry",EPULLGEOM(pull->eGeom));
- pr_ivec(fp,indent,"pull_dim",pull->dim,DIM,TRUE);
- PR("pull_r1",pull->cyl_r1);
- PR("pull_r0",pull->cyl_r0);
- PR("pull_constr_tol",pull->constr_tol);
- PI("pull_nstxout",pull->nstxout);
- PI("pull_nstfout",pull->nstfout);
- PI("pull_ngrp",pull->ngrp);
- for(g=0; g<pull->ngrp+1; g++)
- pr_pullgrp(fp,indent,g,&pull->grp[g]);
-}
-
-void pr_inputrec(FILE *fp,int indent,const char *title,t_inputrec *ir,
- gmx_bool bMDPformat)
-{
- const char *infbuf="inf";
- int i;
-
- if (available(fp,ir,indent,title)) {
- if (!bMDPformat)
- indent=pr_title(fp,indent,title);
- PS("integrator",EI(ir->eI));
- PSTEP("nsteps",ir->nsteps);
- PSTEP("init_step",ir->init_step);
- PS("ns_type",ENS(ir->ns_type));
- PI("nstlist",ir->nstlist);
- PI("ndelta",ir->ndelta);
- PI("nstcomm",ir->nstcomm);
- PS("comm_mode",ECOM(ir->comm_mode));
- PI("nstlog",ir->nstlog);
- PI("nstxout",ir->nstxout);
- PI("nstvout",ir->nstvout);
- PI("nstfout",ir->nstfout);
- PI("nstcalcenergy",ir->nstcalcenergy);
- PI("nstenergy",ir->nstenergy);
- PI("nstxtcout",ir->nstxtcout);
- PR("init_t",ir->init_t);
- PR("delta_t",ir->delta_t);
-
- PR("xtcprec",ir->xtcprec);
- PI("nkx",ir->nkx);
- PI("nky",ir->nky);
- PI("nkz",ir->nkz);
- PI("pme_order",ir->pme_order);
- PR("ewald_rtol",ir->ewald_rtol);
- PR("ewald_geometry",ir->ewald_geometry);
- PR("epsilon_surface",ir->epsilon_surface);
- PS("optimize_fft",BOOL(ir->bOptFFT));
- PS("ePBC",EPBC(ir->ePBC));
- PS("bPeriodicMols",BOOL(ir->bPeriodicMols));
- PS("bContinuation",BOOL(ir->bContinuation));
- PS("bShakeSOR",BOOL(ir->bShakeSOR));
- PS("etc",ETCOUPLTYPE(ir->etc));
- PI("nsttcouple",ir->nsttcouple);
- PS("epc",EPCOUPLTYPE(ir->epc));
- PS("epctype",EPCOUPLTYPETYPE(ir->epct));
- PI("nstpcouple",ir->nstpcouple);
- PR("tau_p",ir->tau_p);
- pr_matrix(fp,indent,"ref_p",ir->ref_p,bMDPformat);
- pr_matrix(fp,indent,"compress",ir->compress,bMDPformat);
- PS("refcoord_scaling",EREFSCALINGTYPE(ir->refcoord_scaling));
- if (bMDPformat)
- fprintf(fp,"posres_com = %g %g %g\n",ir->posres_com[XX],
- ir->posres_com[YY],ir->posres_com[ZZ]);
- else
- pr_rvec(fp,indent,"posres_com",ir->posres_com,DIM,TRUE);
- if (bMDPformat)
- fprintf(fp,"posres_comB = %g %g %g\n",ir->posres_comB[XX],
- ir->posres_comB[YY],ir->posres_comB[ZZ]);
- else
- pr_rvec(fp,indent,"posres_comB",ir->posres_comB,DIM,TRUE);
- PI("andersen_seed",ir->andersen_seed);
- PR("rlist",ir->rlist);
- PR("rlistlong",ir->rlistlong);
- PR("rtpi",ir->rtpi);
- PS("coulombtype",EELTYPE(ir->coulombtype));
- PR("rcoulomb_switch",ir->rcoulomb_switch);
- PR("rcoulomb",ir->rcoulomb);
- PS("vdwtype",EVDWTYPE(ir->vdwtype));
- PR("rvdw_switch",ir->rvdw_switch);
- PR("rvdw",ir->rvdw);
- if (ir->epsilon_r != 0)
- PR("epsilon_r",ir->epsilon_r);
- else
- PS("epsilon_r",infbuf);
- if (ir->epsilon_rf != 0)
- PR("epsilon_rf",ir->epsilon_rf);
- else
- PS("epsilon_rf",infbuf);
- PR("tabext",ir->tabext);
- PS("implicit_solvent",EIMPLICITSOL(ir->implicit_solvent));
- PS("gb_algorithm",EGBALGORITHM(ir->gb_algorithm));
- PR("gb_epsilon_solvent",ir->gb_epsilon_solvent);
- PI("nstgbradii",ir->nstgbradii);
- PR("rgbradii",ir->rgbradii);
- PR("gb_saltconc",ir->gb_saltconc);
- PR("gb_obc_alpha",ir->gb_obc_alpha);
- PR("gb_obc_beta",ir->gb_obc_beta);
- PR("gb_obc_gamma",ir->gb_obc_gamma);
- PR("gb_dielectric_offset",ir->gb_dielectric_offset);
- PS("sa_algorithm",ESAALGORITHM(ir->gb_algorithm));
- PR("sa_surface_tension",ir->sa_surface_tension);
-
- PS("DispCorr",EDISPCORR(ir->eDispCorr));
- PS("free_energy",EFEPTYPE(ir->efep));
- PR("init_lambda",ir->init_lambda);
- PR("delta_lambda",ir->delta_lambda);
- if (!bMDPformat)
- {
- PI("n_foreign_lambda",ir->n_flambda);
- }
- if (ir->n_flambda > 0)
- {
- pr_indent(fp,indent);
- fprintf(fp,"foreign_lambda%s",bMDPformat ? " = " : ":");
- for(i=0; i<ir->n_flambda; i++)
- {
- fprintf(fp," %10g",ir->flambda[i]);
- }
- fprintf(fp,"\n");
- }
- PR("sc_alpha",ir->sc_alpha);
- PI("sc_power",ir->sc_power);
- PR("sc_sigma",ir->sc_sigma);
- PR("sc_sigma_min",ir->sc_sigma_min);
- PI("nstdhdl", ir->nstdhdl);
- PS("separate_dhdl_file", SEPDHDLFILETYPE(ir->separate_dhdl_file));
- PS("dhdl_derivatives", DHDLDERIVATIVESTYPE(ir->dhdl_derivatives));
- PI("dh_hist_size", ir->dh_hist_size);
- PD("dh_hist_spacing", ir->dh_hist_spacing);
-
- PI("nwall",ir->nwall);
- PS("wall_type",EWALLTYPE(ir->wall_type));
- PI("wall_atomtype[0]",ir->wall_atomtype[0]);
- PI("wall_atomtype[1]",ir->wall_atomtype[1]);
- PR("wall_density[0]",ir->wall_density[0]);
- PR("wall_density[1]",ir->wall_density[1]);
- PR("wall_ewald_zfac",ir->wall_ewald_zfac);
-
- PS("pull",EPULLTYPE(ir->ePull));
- if (ir->ePull != epullNO)
- pr_pull(fp,indent,ir->pull);
-
- PS("disre",EDISRETYPE(ir->eDisre));
- PS("disre_weighting",EDISREWEIGHTING(ir->eDisreWeighting));
- PS("disre_mixed",BOOL(ir->bDisreMixed));
- PR("dr_fc",ir->dr_fc);
- PR("dr_tau",ir->dr_tau);
- PR("nstdisreout",ir->nstdisreout);
- PR("orires_fc",ir->orires_fc);
- PR("orires_tau",ir->orires_tau);
- PR("nstorireout",ir->nstorireout);
-
- PR("dihre-fc",ir->dihre_fc);
-
- PR("em_stepsize",ir->em_stepsize);
- PR("em_tol",ir->em_tol);
- PI("niter",ir->niter);
- PR("fc_stepsize",ir->fc_stepsize);
- PI("nstcgsteep",ir->nstcgsteep);
- PI("nbfgscorr",ir->nbfgscorr);
-
- PS("ConstAlg",ECONSTRTYPE(ir->eConstrAlg));
- PR("shake_tol",ir->shake_tol);
- PI("lincs_order",ir->nProjOrder);
- PR("lincs_warnangle",ir->LincsWarnAngle);
- PI("lincs_iter",ir->nLincsIter);
- PR("bd_fric",ir->bd_fric);
- PI("ld_seed",ir->ld_seed);
- PR("cos_accel",ir->cos_accel);
- pr_matrix(fp,indent,"deform",ir->deform,bMDPformat);
- PI("userint1",ir->userint1);
- PI("userint2",ir->userint2);
- PI("userint3",ir->userint3);
- PI("userint4",ir->userint4);
- PR("userreal1",ir->userreal1);
- PR("userreal2",ir->userreal2);
- PR("userreal3",ir->userreal3);
- PR("userreal4",ir->userreal4);
- pr_grp_opts(fp,indent,"grpopts",&(ir->opts),bMDPformat);
- pr_cosine(fp,indent,"efield-x",&(ir->ex[XX]),bMDPformat);
- pr_cosine(fp,indent,"efield-xt",&(ir->et[XX]),bMDPformat);
- pr_cosine(fp,indent,"efield-y",&(ir->ex[YY]),bMDPformat);
- pr_cosine(fp,indent,"efield-yt",&(ir->et[YY]),bMDPformat);
- pr_cosine(fp,indent,"efield-z",&(ir->ex[ZZ]),bMDPformat);
- pr_cosine(fp,indent,"efield-zt",&(ir->et[ZZ]),bMDPformat);
- PS("bQMMM",BOOL(ir->bQMMM));
- PI("QMconstraints",ir->QMconstraints);
- PI("QMMMscheme",ir->QMMMscheme);
- PR("scalefactor",ir->scalefactor);
- pr_qm_opts(fp,indent,"qm_opts",&(ir->opts));
- }
-}
-#undef PS
-#undef PR
-#undef PI
-
-static void pr_harm(FILE *fp,t_iparams *iparams,const char *r,const char *kr)
-{
- fprintf(fp,"%sA=%12.5e, %sA=%12.5e, %sB=%12.5e, %sB=%12.5e\n",
- r,iparams->harmonic.rA,kr,iparams->harmonic.krA,
- r,iparams->harmonic.rB,kr,iparams->harmonic.krB);
-}
-
-void pr_iparams(FILE *fp,t_functype ftype,t_iparams *iparams)
-{
- int i;
- real VA[4],VB[4],*rbcA,*rbcB;
-
- switch (ftype) {
- case F_ANGLES:
- case F_G96ANGLES:
- pr_harm(fp,iparams,"th","ct");
- break;
- case F_CROSS_BOND_BONDS:
- fprintf(fp,"r1e=%15.8e, r2e=%15.8e, krr=%15.8e\n",
- iparams->cross_bb.r1e,iparams->cross_bb.r2e,
- iparams->cross_bb.krr);
- break;
- case F_CROSS_BOND_ANGLES:
- fprintf(fp,"r1e=%15.8e, r1e=%15.8e, r3e=%15.8e, krt=%15.8e\n",
- iparams->cross_ba.r1e,iparams->cross_ba.r2e,
- iparams->cross_ba.r3e,iparams->cross_ba.krt);
- break;
- case F_UREY_BRADLEY:
- fprintf(fp,"theta=%15.8e, ktheta=%15.8e, r13=%15.8e, kUB=%15.8e\n",
- iparams->u_b.theta,iparams->u_b.ktheta,iparams->u_b.r13,iparams->u_b.kUB);
- break;
- case F_QUARTIC_ANGLES:
- fprintf(fp,"theta=%15.8e",iparams->qangle.theta);
- for(i=0; i<5; i++)
- fprintf(fp,", c%c=%15.8e",'0'+i,iparams->qangle.c[i]);
- fprintf(fp,"\n");
- break;
- case F_BHAM:
- fprintf(fp,"a=%15.8e, b=%15.8e, c=%15.8e\n",
- iparams->bham.a,iparams->bham.b,iparams->bham.c);
- break;
- case F_BONDS:
- case F_G96BONDS:
- case F_HARMONIC:
- pr_harm(fp,iparams,"b0","cb");
- break;
- case F_IDIHS:
- pr_harm(fp,iparams,"xi","cx");
- break;
- case F_MORSE:
- fprintf(fp,"b0=%15.8e, cb=%15.8e, beta=%15.8e\n",
- iparams->morse.b0,iparams->morse.cb,iparams->morse.beta);
- break;
- case F_CUBICBONDS:
- fprintf(fp,"b0=%15.8e, kb=%15.8e, kcub=%15.8e\n",
- iparams->cubic.b0,iparams->cubic.kb,iparams->cubic.kcub);
- break;
- case F_CONNBONDS:
- fprintf(fp,"\n");
- break;
- case F_FENEBONDS:
- fprintf(fp,"bm=%15.8e, kb=%15.8e\n",iparams->fene.bm,iparams->fene.kb);
- break;
- case F_RESTRBONDS:
- fprintf(fp,"lowA=%15.8e, up1A=%15.8e, up2A=%15.8e, kA=%15.8e, lowB=%15.8e, up1B=%15.8e, up2B=%15.8e, kB=%15.8e,\n",
- iparams->restraint.lowA,iparams->restraint.up1A,
- iparams->restraint.up2A,iparams->restraint.kA,
- iparams->restraint.lowB,iparams->restraint.up1B,
- iparams->restraint.up2B,iparams->restraint.kB);
- break;
- case F_TABBONDS:
- case F_TABBONDSNC:
- case F_TABANGLES:
- case F_TABDIHS:
- fprintf(fp,"tab=%d, kA=%15.8e, kB=%15.8e\n",
- iparams->tab.table,iparams->tab.kA,iparams->tab.kB);
- break;
- case F_POLARIZATION:
- fprintf(fp,"alpha=%15.8e\n",iparams->polarize.alpha);
- break;
- case F_THOLE_POL:
- fprintf(fp,"a=%15.8e, alpha1=%15.8e, alpha2=%15.8e, rfac=%15.8e\n",
- iparams->thole.a,iparams->thole.alpha1,iparams->thole.alpha2,
- iparams->thole.rfac);
- break;
- case F_WATER_POL:
- fprintf(fp,"al_x=%15.8e, al_y=%15.8e, al_z=%15.8e, rOH=%9.6f, rHH=%9.6f, rOD=%9.6f\n",
- iparams->wpol.al_x,iparams->wpol.al_y,iparams->wpol.al_z,
- iparams->wpol.rOH,iparams->wpol.rHH,iparams->wpol.rOD);
- break;
- case F_LJ:
- fprintf(fp,"c6=%15.8e, c12=%15.8e\n",iparams->lj.c6,iparams->lj.c12);
- break;
- case F_LJ14:
- fprintf(fp,"c6A=%15.8e, c12A=%15.8e, c6B=%15.8e, c12B=%15.8e\n",
- iparams->lj14.c6A,iparams->lj14.c12A,
- iparams->lj14.c6B,iparams->lj14.c12B);
- break;
- case F_LJC14_Q:
- fprintf(fp,"fqq=%15.8e, qi=%15.8e, qj=%15.8e, c6=%15.8e, c12=%15.8e\n",
- iparams->ljc14.fqq,
- iparams->ljc14.qi,iparams->ljc14.qj,
- iparams->ljc14.c6,iparams->ljc14.c12);
- break;
- case F_LJC_PAIRS_NB:
- fprintf(fp,"qi=%15.8e, qj=%15.8e, c6=%15.8e, c12=%15.8e\n",
- iparams->ljcnb.qi,iparams->ljcnb.qj,
- iparams->ljcnb.c6,iparams->ljcnb.c12);
- break;
- case F_PDIHS:
- case F_PIDIHS:
- case F_ANGRES:
- case F_ANGRESZ:
- fprintf(fp,"phiA=%15.8e, cpA=%15.8e, phiB=%15.8e, cpB=%15.8e, mult=%d\n",
- iparams->pdihs.phiA,iparams->pdihs.cpA,
- iparams->pdihs.phiB,iparams->pdihs.cpB,
- iparams->pdihs.mult);
- break;
- case F_DISRES:
- fprintf(fp,"label=%4d, type=%1d, low=%15.8e, up1=%15.8e, up2=%15.8e, fac=%15.8e)\n",
- iparams->disres.label,iparams->disres.type,
- iparams->disres.low,iparams->disres.up1,
- iparams->disres.up2,iparams->disres.kfac);
- break;
- case F_ORIRES:
- fprintf(fp,"ex=%4d, label=%d, power=%4d, c=%15.8e, obs=%15.8e, kfac=%15.8e)\n",
- iparams->orires.ex,iparams->orires.label,iparams->orires.power,
- iparams->orires.c,iparams->orires.obs,iparams->orires.kfac);
- break;
- case F_DIHRES:
- fprintf(fp,"label=%d, power=%4d phi=%15.8e, dphi=%15.8e, kfac=%15.8e)\n",
- iparams->dihres.label,iparams->dihres.power,
- iparams->dihres.phi,iparams->dihres.dphi,iparams->dihres.kfac);
- break;
- case F_POSRES:
- fprintf(fp,"pos0A=(%15.8e,%15.8e,%15.8e), fcA=(%15.8e,%15.8e,%15.8e), pos0B=(%15.8e,%15.8e,%15.8e), fcB=(%15.8e,%15.8e,%15.8e)\n",
- iparams->posres.pos0A[XX],iparams->posres.pos0A[YY],
- iparams->posres.pos0A[ZZ],iparams->posres.fcA[XX],
- iparams->posres.fcA[YY],iparams->posres.fcA[ZZ],
- iparams->posres.pos0B[XX],iparams->posres.pos0B[YY],
- iparams->posres.pos0B[ZZ],iparams->posres.fcB[XX],
- iparams->posres.fcB[YY],iparams->posres.fcB[ZZ]);
- break;
- case F_RBDIHS:
- for (i=0; i<NR_RBDIHS; i++)
- fprintf(fp,"%srbcA[%d]=%15.8e",i==0?"":", ",i,iparams->rbdihs.rbcA[i]);
- fprintf(fp,"\n");
- for (i=0; i<NR_RBDIHS; i++)
- fprintf(fp,"%srbcB[%d]=%15.8e",i==0?"":", ",i,iparams->rbdihs.rbcB[i]);
- fprintf(fp,"\n");
- break;
- case F_FOURDIHS:
- /* Use the OPLS -> Ryckaert-Bellemans formula backwards to get the
- * OPLS potential constants back.
- */
- rbcA = iparams->rbdihs.rbcA;
- rbcB = iparams->rbdihs.rbcB;
-
- VA[3] = -0.25*rbcA[4];
- VA[2] = -0.5*rbcA[3];
- VA[1] = 4.0*VA[3]-rbcA[2];
- VA[0] = 3.0*VA[2]-2.0*rbcA[1];
-
- VB[3] = -0.25*rbcB[4];
- VB[2] = -0.5*rbcB[3];
- VB[1] = 4.0*VB[3]-rbcB[2];
- VB[0] = 3.0*VB[2]-2.0*rbcB[1];
-
- for (i=0; i<NR_FOURDIHS; i++)
- fprintf(fp,"%sFourA[%d]=%15.8e",i==0?"":", ",i,VA[i]);
- fprintf(fp,"\n");
- for (i=0; i<NR_FOURDIHS; i++)
- fprintf(fp,"%sFourB[%d]=%15.8e",i==0?"":", ",i,VB[i]);
- fprintf(fp,"\n");
- break;
-
- case F_CONSTR:
- case F_CONSTRNC:
- fprintf(fp,"dA=%15.8e, dB=%15.8e\n",iparams->constr.dA,iparams->constr.dB);
- break;
- case F_SETTLE:
- fprintf(fp,"doh=%15.8e, dhh=%15.8e\n",iparams->settle.doh,
- iparams->settle.dhh);
- break;
- case F_VSITE2:
- fprintf(fp,"a=%15.8e\n",iparams->vsite.a);
- break;
- case F_VSITE3:
- case F_VSITE3FD:
- case F_VSITE3FAD:
- fprintf(fp,"a=%15.8e, b=%15.8e\n",iparams->vsite.a,iparams->vsite.b);
- break;
- case F_VSITE3OUT:
- case F_VSITE4FD:
- case F_VSITE4FDN:
- fprintf(fp,"a=%15.8e, b=%15.8e, c=%15.8e\n",
- iparams->vsite.a,iparams->vsite.b,iparams->vsite.c);
- break;
- case F_VSITEN:
- fprintf(fp,"n=%2d, a=%15.8e\n",iparams->vsiten.n,iparams->vsiten.a);
- break;
- case F_GB12:
- case F_GB13:
- case F_GB14:
- fprintf(fp, "sar=%15.8e, st=%15.8e, pi=%15.8e, gbr=%15.8e, bmlt=%15.8e\n",iparams->gb.sar,iparams->gb.st,iparams->gb.pi,iparams->gb.gbr,iparams->gb.bmlt);
- break;
- case F_CMAP:
- fprintf(fp, "cmapA=%1d, cmapB=%1d\n",iparams->cmap.cmapA, iparams->cmap.cmapB);
- break;
- default:
- gmx_fatal(FARGS,"unknown function type %d (%s) in %s line %d",
- ftype,interaction_function[ftype].name,__FILE__,__LINE__);
- }
-}
-
-void pr_ilist(FILE *fp,int indent,const char *title,
- t_functype *functype,t_ilist *ilist, gmx_bool bShowNumbers)
-{
- int i,j,k,type,ftype;
- t_iatom *iatoms;
-
- if (available(fp,ilist,indent,title) && ilist->nr > 0)
- {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- fprintf(fp,"nr: %d\n",ilist->nr);
- if (ilist->nr > 0) {
- (void) pr_indent(fp,indent);
- fprintf(fp,"iatoms:\n");
- iatoms=ilist->iatoms;
- for (i=j=0; i<ilist->nr;) {
-#ifndef DEBUG
- (void) pr_indent(fp,indent+INDENT);
- type=*(iatoms++);
- ftype=functype[type];
- (void) fprintf(fp,"%d type=%d (%s)",
- bShowNumbers?j:-1,bShowNumbers?type:-1,
- interaction_function[ftype].name);
- j++;
- for (k=0; k<interaction_function[ftype].nratoms; k++)
- (void) fprintf(fp," %u",*(iatoms++));
- (void) fprintf(fp,"\n");
- i+=1+interaction_function[ftype].nratoms;
-#else
- fprintf(fp,"%5d%5d\n",i,iatoms[i]);
- i++;
-#endif
- }
- }
- }
-}
-
-static void pr_cmap(FILE *fp, int indent, const char *title,
- gmx_cmap_t *cmap_grid, gmx_bool bShowNumbers)
-{
- int i,j,nelem;
- real dx,idx;
-
- dx = 360.0 / cmap_grid->grid_spacing;
- nelem = cmap_grid->grid_spacing*cmap_grid->grid_spacing;
-
- if(available(fp,cmap_grid,indent,title))
- {
- fprintf(fp,"%s\n",title);
-
- for(i=0;i<cmap_grid->ngrid;i++)
- {
- idx = -180.0;
- fprintf(fp,"%8s %8s %8s %8s\n","V","dVdx","dVdy","d2dV");
-
- fprintf(fp,"grid[%3d]={\n",bShowNumbers?i:-1);
-
- for(j=0;j<nelem;j++)
- {
- if( (j%cmap_grid->grid_spacing)==0)
- {
- fprintf(fp,"%8.1f\n",idx);
- idx+=dx;
- }
-
- fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4]);
- fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4+1]);
- fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4+2]);
- fprintf(fp,"%8.3f\n",cmap_grid->cmapdata[i].cmap[j*4+3]);
- }
- fprintf(fp,"\n");
- }
- }
-
-}
-
-void pr_ffparams(FILE *fp,int indent,const char *title,
- gmx_ffparams_t *ffparams,
- gmx_bool bShowNumbers)
-{
- int i,j;
-
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"atnr=%d\n",ffparams->atnr);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"ntypes=%d\n",ffparams->ntypes);
- for (i=0; i<ffparams->ntypes; i++) {
- (void) pr_indent(fp,indent+INDENT);
- (void) fprintf(fp,"functype[%d]=%s, ",
- bShowNumbers?i:-1,
- interaction_function[ffparams->functype[i]].name);
- pr_iparams(fp,ffparams->functype[i],&ffparams->iparams[i]);
- }
- (void) pr_double(fp,indent,"reppow",ffparams->reppow);
- (void) pr_real(fp,indent,"fudgeQQ",ffparams->fudgeQQ);
- pr_cmap(fp,indent,"cmap",&ffparams->cmap_grid,bShowNumbers);
-}
-
-void pr_idef(FILE *fp,int indent,const char *title,t_idef *idef, gmx_bool bShowNumbers)
-{
- int i,j;
-
- if (available(fp,idef,indent,title)) {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"atnr=%d\n",idef->atnr);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"ntypes=%d\n",idef->ntypes);
- for (i=0; i<idef->ntypes; i++) {
- (void) pr_indent(fp,indent+INDENT);
- (void) fprintf(fp,"functype[%d]=%s, ",
- bShowNumbers?i:-1,
- interaction_function[idef->functype[i]].name);
- pr_iparams(fp,idef->functype[i],&idef->iparams[i]);
- }
- (void) pr_real(fp,indent,"fudgeQQ",idef->fudgeQQ);
-
- for(j=0; (j<F_NRE); j++)
- pr_ilist(fp,indent,interaction_function[j].longname,
- idef->functype,&idef->il[j],bShowNumbers);
- }
-}
-
-static int pr_block_title(FILE *fp,int indent,const char *title,t_block *block)
-{
- int i;
-
- if (available(fp,block,indent,title))
- {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"nr=%d\n",block->nr);
- }
- return indent;
-}
-
-static int pr_blocka_title(FILE *fp,int indent,const char *title,t_blocka *block)
-{
- int i;
-
- if (available(fp,block,indent,title))
- {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"nr=%d\n",block->nr);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"nra=%d\n",block->nra);
- }
- return indent;
-}
-
-static void low_pr_block(FILE *fp,int indent,const char *title,t_block *block, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,block,indent,title))
- {
- indent=pr_block_title(fp,indent,title,block);
- for (i=0; i<=block->nr; i++)
- {
- (void) pr_indent(fp,indent+INDENT);
- (void) fprintf(fp,"%s->index[%d]=%u\n",
- title,bShowNumbers?i:-1,block->index[i]);
- }
- }
-}
-
-static void low_pr_blocka(FILE *fp,int indent,const char *title,t_blocka *block, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,block,indent,title))
- {
- indent=pr_blocka_title(fp,indent,title,block);
- for (i=0; i<=block->nr; i++)
- {
- (void) pr_indent(fp,indent+INDENT);
- (void) fprintf(fp,"%s->index[%d]=%u\n",
- title,bShowNumbers?i:-1,block->index[i]);
- }
- for (i=0; i<block->nra; i++)
- {
- (void) pr_indent(fp,indent+INDENT);
- (void) fprintf(fp,"%s->a[%d]=%u\n",
- title,bShowNumbers?i:-1,block->a[i]);
- }
- }
-}
-
-void pr_block(FILE *fp,int indent,const char *title,t_block *block,gmx_bool bShowNumbers)
-{
- int i,j,ok,size,start,end;
-
- if (available(fp,block,indent,title))
- {
- indent=pr_block_title(fp,indent,title,block);
- start=0;
- end=start;
- if ((ok=(block->index[start]==0))==0)
- (void) fprintf(fp,"block->index[%d] should be 0\n",start);
- else
- for (i=0; i<block->nr; i++)
- {
- end=block->index[i+1];
- size=pr_indent(fp,indent);
- if (end<=start)
- size+=fprintf(fp,"%s[%d]={}\n",title,i);
- else
- size+=fprintf(fp,"%s[%d]={%d..%d}\n",
- title,bShowNumbers?i:-1,
- bShowNumbers?start:-1,bShowNumbers?end-1:-1);
- start=end;
- }
- }
-}
-
-void pr_blocka(FILE *fp,int indent,const char *title,t_blocka *block,gmx_bool bShowNumbers)
-{
- int i,j,ok,size,start,end;
-
- if (available(fp,block,indent,title))
- {
- indent=pr_blocka_title(fp,indent,title,block);
- start=0;
- end=start;
- if ((ok=(block->index[start]==0))==0)
- (void) fprintf(fp,"block->index[%d] should be 0\n",start);
- else
- for (i=0; i<block->nr; i++)
- {
- end=block->index[i+1];
- size=pr_indent(fp,indent);
- if (end<=start)
- size+=fprintf(fp,"%s[%d]={",title,i);
- else
- size+=fprintf(fp,"%s[%d][%d..%d]={",
- title,bShowNumbers?i:-1,
- bShowNumbers?start:-1,bShowNumbers?end-1:-1);
- for (j=start; j<end; j++)
- {
- if (j>start) size+=fprintf(fp,", ");
- if ((size)>(USE_WIDTH))
- {
- (void) fprintf(fp,"\n");
- size=pr_indent(fp,indent+INDENT);
- }
- size+=fprintf(fp,"%u",block->a[j]);
- }
- (void) fprintf(fp,"}\n");
- start=end;
- }
- if ((end!=block->nra)||(!ok))
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"tables inconsistent, dumping complete tables:\n");
- low_pr_blocka(fp,indent,title,block,bShowNumbers);
- }
- }
-}
-
-static void pr_strings(FILE *fp,int indent,const char *title,char ***nm,int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,nm,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]={name=\"%s\"}\n",
- title,bShowNumbers?i:-1,*(nm[i]));
- }
- }
-}
-
-static void pr_strings2(FILE *fp,int indent,const char *title,
- char ***nm,char ***nmB,int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,nm,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]={name=\"%s\",nameB=\"%s\"}\n",
- title,bShowNumbers?i:-1,*(nm[i]),*(nmB[i]));
- }
- }
-}
-
-static void pr_resinfo(FILE *fp,int indent,const char *title,t_resinfo *resinfo,int n, gmx_bool bShowNumbers)
-{
- int i;
-
- if (available(fp,resinfo,indent,title))
- {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++)
- {
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%s[%d]={name=\"%s\", nr=%d, ic='%c'}\n",
- title,bShowNumbers?i:-1,
- *(resinfo[i].name),resinfo[i].nr,
- (resinfo[i].ic == '\0') ? ' ' : resinfo[i].ic);
- }
- }
-}
-
-static void pr_atom(FILE *fp,int indent,const char *title,t_atom *atom,int n)
-{
- int i,j;
-
- if (available(fp,atom,indent,title)) {
- indent=pr_title_n(fp,indent,title,n);
- for (i=0; i<n; i++) {
- (void) pr_indent(fp,indent);
- fprintf(fp,"%s[%6d]={type=%3d, typeB=%3d, ptype=%8s, m=%12.5e, "
- "q=%12.5e, mB=%12.5e, qB=%12.5e, resind=%5d, atomnumber=%3d}\n",
- title,i,atom[i].type,atom[i].typeB,ptype_str[atom[i].ptype],
- atom[i].m,atom[i].q,atom[i].mB,atom[i].qB,
- atom[i].resind,atom[i].atomnumber);
- }
- }
-}
-
-static void pr_grps(FILE *fp,int indent,const char *title,t_grps grps[],
- char **grpname[], gmx_bool bShowNumbers)
-{
- int i,j;
-
- for(i=0; (i<egcNR); i++)
- {
- fprintf(fp,"%s[%-12s] nr=%d, name=[",title,gtypes[i],grps[i].nr);
- for(j=0; (j<grps[i].nr); j++)
- {
- fprintf(fp," %s",*(grpname[grps[i].nm_ind[j]]));
- }
- fprintf(fp,"]\n");
- }
-}
-
-static void pr_groups(FILE *fp,int indent,const char *title,
- gmx_groups_t *groups,
- gmx_bool bShowNumbers)
-{
- int grpnr[egcNR];
- int nat_max,i,g;
-
- pr_grps(fp,indent,"grp",groups->grps,groups->grpname,bShowNumbers);
- pr_strings(fp,indent,"grpname",groups->grpname,groups->ngrpname,bShowNumbers);
-
- (void) pr_indent(fp,indent);
- fprintf(fp,"groups ");
- for(g=0; g<egcNR; g++)
- {
- printf(" %5.5s",gtypes[g]);
- }
- printf("\n");
-
- (void) pr_indent(fp,indent);
- fprintf(fp,"allocated ");
- nat_max = 0;
- for(g=0; g<egcNR; g++)
- {
- printf(" %5d",groups->ngrpnr[g]);
- nat_max = max(nat_max,groups->ngrpnr[g]);
- }
- printf("\n");
-
- if (nat_max == 0)
- {
- (void) pr_indent(fp,indent);
- fprintf(fp,"groupnr[%5s] =","*");
- for(g=0; g<egcNR; g++)
- {
- fprintf(fp," %3d ",0);
- }
- fprintf(fp,"\n");
- }
- else
- {
- for(i=0; i<nat_max; i++)
- {
- (void) pr_indent(fp,indent);
- fprintf(fp,"groupnr[%5d] =",i);
- for(g=0; g<egcNR; g++)
- {
- fprintf(fp," %3d ",
- groups->grpnr[g] ? groups->grpnr[g][i] : 0);
- }
- fprintf(fp,"\n");
- }
- }
-}
-
-void pr_atoms(FILE *fp,int indent,const char *title,t_atoms *atoms,
- gmx_bool bShownumbers)
-{
- if (available(fp,atoms,indent,title))
- {
- indent=pr_title(fp,indent,title);
- pr_atom(fp,indent,"atom",atoms->atom,atoms->nr);
- pr_strings(fp,indent,"atom",atoms->atomname,atoms->nr,bShownumbers);
- pr_strings2(fp,indent,"type",atoms->atomtype,atoms->atomtypeB,atoms->nr,bShownumbers);
- pr_resinfo(fp,indent,"residue",atoms->resinfo,atoms->nres,bShownumbers);
- }
-}
-
-
-void pr_atomtypes(FILE *fp,int indent,const char *title,t_atomtypes *atomtypes,
- gmx_bool bShowNumbers)
-{
- int i;
- if (available(fp,atomtypes,indent,title))
- {
- indent=pr_title(fp,indent,title);
- for(i=0;i<atomtypes->nr;i++) {
- pr_indent(fp,indent);
- fprintf(fp,
- "atomtype[%3d]={radius=%12.5e, volume=%12.5e, gb_radius=%12.5e, surftens=%12.5e, atomnumber=%4d, S_hct=%12.5e)}\n",
- bShowNumbers?i:-1,atomtypes->radius[i],atomtypes->vol[i],
- atomtypes->gb_radius[i],
- atomtypes->surftens[i],atomtypes->atomnumber[i],atomtypes->S_hct[i]);
- }
- }
-}
-
-static void pr_moltype(FILE *fp,int indent,const char *title,
- gmx_moltype_t *molt,int n,
- gmx_ffparams_t *ffparams,
- gmx_bool bShowNumbers)
-{
- int j;
-
- indent = pr_title_n(fp,indent,title,n);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"name=\"%s\"\n",*(molt->name));
- pr_atoms(fp,indent,"atoms",&(molt->atoms),bShowNumbers);
- pr_block(fp,indent,"cgs",&molt->cgs, bShowNumbers);
- pr_blocka(fp,indent,"excls",&molt->excls, bShowNumbers);
- for(j=0; (j<F_NRE); j++) {
- pr_ilist(fp,indent,interaction_function[j].longname,
- ffparams->functype,&molt->ilist[j],bShowNumbers);
- }
-}
-
-static void pr_molblock(FILE *fp,int indent,const char *title,
- gmx_molblock_t *molb,int n,
- gmx_moltype_t *molt,
- gmx_bool bShowNumbers)
-{
- indent = pr_title_n(fp,indent,title,n);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"%-20s = %d \"%s\"\n",
- "moltype",molb->type,*(molt[molb->type].name));
- pr_int(fp,indent,"#molecules",molb->nmol);
- pr_int(fp,indent,"#atoms_mol",molb->natoms_mol);
- pr_int(fp,indent,"#posres_xA",molb->nposres_xA);
- if (molb->nposres_xA > 0) {
- pr_rvecs(fp,indent,"posres_xA",molb->posres_xA,molb->nposres_xA);
- }
- pr_int(fp,indent,"#posres_xB",molb->nposres_xB);
- if (molb->nposres_xB > 0) {
- pr_rvecs(fp,indent,"posres_xB",molb->posres_xB,molb->nposres_xB);
- }
-}
-
-void pr_mtop(FILE *fp,int indent,const char *title,gmx_mtop_t *mtop,
- gmx_bool bShowNumbers)
-{
- int mt,mb;
-
- if (available(fp,mtop,indent,title)) {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"name=\"%s\"\n",*(mtop->name));
- pr_int(fp,indent,"#atoms",mtop->natoms);
- for(mb=0; mb<mtop->nmolblock; mb++) {
- pr_molblock(fp,indent,"molblock",&mtop->molblock[mb],mb,
- mtop->moltype,bShowNumbers);
- }
- pr_ffparams(fp,indent,"ffparams",&(mtop->ffparams),bShowNumbers);
- pr_atomtypes(fp,indent,"atomtypes",&(mtop->atomtypes),bShowNumbers);
- for(mt=0; mt<mtop->nmoltype; mt++) {
- pr_moltype(fp,indent,"moltype",&mtop->moltype[mt],mt,
- &mtop->ffparams,bShowNumbers);
- }
- pr_groups(fp,indent,"groups",&mtop->groups,bShowNumbers);
- }
-}
-
-void pr_top(FILE *fp,int indent,const char *title,t_topology *top, gmx_bool bShowNumbers)
-{
- if (available(fp,top,indent,title)) {
- indent=pr_title(fp,indent,title);
- (void) pr_indent(fp,indent);
- (void) fprintf(fp,"name=\"%s\"\n",*(top->name));
- pr_atoms(fp,indent,"atoms",&(top->atoms),bShowNumbers);
- pr_atomtypes(fp,indent,"atomtypes",&(top->atomtypes),bShowNumbers);
- pr_block(fp,indent,"cgs",&top->cgs, bShowNumbers);
- pr_block(fp,indent,"mols",&top->mols, bShowNumbers);
- pr_blocka(fp,indent,"excls",&top->excls, bShowNumbers);
- pr_idef(fp,indent,"idef",&top->idef,bShowNumbers);
- }
-}
-
-void pr_header(FILE *fp,int indent,const char *title,t_tpxheader *sh)
-{
- char buf[22];
-
- if (available(fp,sh,indent,title))
- {
- indent=pr_title(fp,indent,title);
- pr_indent(fp,indent);
- fprintf(fp,"bIr = %spresent\n",sh->bIr?"":"not ");
- pr_indent(fp,indent);
- fprintf(fp,"bBox = %spresent\n",sh->bBox?"":"not ");
- pr_indent(fp,indent);
- fprintf(fp,"bTop = %spresent\n",sh->bTop?"":"not ");
- pr_indent(fp,indent);
- fprintf(fp,"bX = %spresent\n",sh->bX?"":"not ");
- pr_indent(fp,indent);
- fprintf(fp,"bV = %spresent\n",sh->bV?"":"not ");
- pr_indent(fp,indent);
- fprintf(fp,"bF = %spresent\n",sh->bF?"":"not ");
-
- pr_indent(fp,indent);
- fprintf(fp,"natoms = %d\n",sh->natoms);
- pr_indent(fp,indent);
- fprintf(fp,"lambda = %e\n",sh->lambda);
- }
-}
-
-void pr_commrec(FILE *fp,int indent,t_commrec *cr)
-{
- pr_indent(fp,indent);
- fprintf(fp,"commrec:\n");
- indent+=2;
- pr_indent(fp,indent);
- fprintf(fp,"nodeid = %d\n",cr->nodeid);
- pr_indent(fp,indent);
- fprintf(fp,"nnodes = %d\n",cr->nnodes);
- pr_indent(fp,indent);
- fprintf(fp,"npmenodes = %d\n",cr->npmenodes);
- /*
- pr_indent(fp,indent);
- fprintf(fp,"threadid = %d\n",cr->threadid);
- pr_indent(fp,indent);
- fprintf(fp,"nthreads = %d\n",cr->nthreads);
- */
-}
+++ /dev/null
-#include "version.h"
-const char _gmx_ver_string[] = "VERSION @GMX_PROJECT_VERSION_STR@";
-const char _gmx_full_git_hash[] = "@GMX_GIT_HEAD_HASH@";
-const char _gmx_central_base_hash[] = "@GMX_GIT_REMOTE_HASH@";
--- /dev/null
+set(LIBGROMACS_SOURCES)
+
+add_subdirectory(legacyheaders)
+add_subdirectory(gmxlib)
+add_subdirectory(mdlib)
+add_subdirectory(analysisdata)
+add_subdirectory(errorreporting)
+add_subdirectory(fatalerror)
+add_subdirectory(options)
+add_subdirectory(selection)
+add_subdirectory(trajectoryanalysis)
+
+file(GLOB LIBGROMACS_HEADERS *.h)
+install(FILES ${LIBGROMACS_HEADERS} DESTINATION ${INCL_INSTALL_DIR}/gromacs
+ COMPONENT development)
+
+# only fiddle with assembly kernels if we're not doing OpenMM build
+if(NOT GMX_OPENMM)
+if(GMX_ASM_USEASM-NASM)
+ enable_language(ASM-NASM)
+ # if NASM is used, we need a special build command for windows...
+ FOREACH(SRC ${GMX_SSEKERNEL_ASM_SRC})
+ GET_FILENAME_COMPONENT(FILE_BASE ${SRC} NAME_WE)
+ SET(OBJ ${CMAKE_CURRENT_BINARY_DIR}/${FILE_BASE}${CMAKE_C_OUTPUT_EXTENSION})
+
+ ADD_CUSTOM_COMMAND(OUTPUT ${OBJ}
+ MAIN_DEPENDENCY ${SRC}
+ COMMAND ${CMAKE_ASM-NASM_COMPILER} -f ${CMAKE_ASM-NASM_OBJECT_FORMAT} -o ${OBJ} ${SRC})
+
+ SET(ALL_ASM_OBJS ${ALL_ASM_OBJS} ${OBJ})
+ ENDFOREACH(SRC ${GMX_SSEKERNEL_ASM_SRC})
+ set(GMX_SSEKERNEL_ASM_SRC ${ALL_ASM_OBJS})
+else(GMX_ASM_USEASM-NASM)
+ enable_language(ASM-ATT)
+ SET(CMAKE_ASM-ATT_COMPILER ${CMAKE_C_COMPILER})
+ if(GMX_IA32_ASM)
+ set_source_files_properties(${GMX_SSEKERNEL_ASM_SRC} PROPERTIES COMPILE_FLAGS "-c -m32")
+ else()
+ set_source_files_properties(${GMX_SSEKERNEL_ASM_SRC} PROPERTIES COMPILE_FLAGS "-c -m64")
+ endif()
+endif(GMX_ASM_USEASM-NASM)
+endif(NOT GMX_OPENMM)
+
+list(APPEND LIBGROMACS_SOURCES ${GMXLIB_SOURCES} ${GMX_SSEKERNEL_ASM_SRC} ${MDLIB_SOURCES})
+
+# add target that generates version.c every time a make is run
+# only do this if we generate the version
+if (USE_VERSION_H)
+ add_custom_target(gmx_version ALL
+ COMMAND ${CMAKE_COMMAND}
+ -D Git_EXECUTABLE="${Git_EXECUTABLE}"
+ -D Git_VERSION="${Git_VERSION}"
+ -D PROJECT_VERSION="${PROJECT_VERSION}"
+ -D PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}"
+ -D VERSION_C_CMAKEIN="${CMAKE_CURRENT_SOURCE_DIR}/version.c.cmakein"
+ -D VERSION_C_OUT="${CMAKE_CURRENT_BINARY_DIR}/version.c"
+ -P ${CMAKE_SOURCE_DIR}/cmake/gmxGenerateVersionInfo.cmake
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/gmxlib
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/version.c.cmakein
+ COMMENT "Generating version information")
+ list(APPEND LIBGROMACS_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/version.c) # auto-generated
+ set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/version.c
+ PROPERTIES GENERATED true)
+endif (USE_VERSION_H)
+
+add_library(libgromacs ${LIBGROMACS_SOURCES})
+if (USE_VERSION_H)
+ add_dependencies(libgromacs gmx_version)
+endif (USE_VERSION_H)
+target_link_libraries(libgromacs
+ ${GMX_EXTRA_LIBRARIES} ${FFT_LIBRARIES} ${XML_LIBRARIES}
+ ${THREAD_LIB})
+set_target_properties(libgromacs PROPERTIES
+ OUTPUT_NAME "gromacs${GMX_LIBS_SUFFIX}"
+ SOVERSION ${SOVERSION}
+ INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
+
+install(TARGETS libgromacs DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgromacs.pc.cmakein
+ ${CMAKE_CURRENT_BINARY_DIR}/libgromacs.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgromacs.pc
+ DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
+ RENAME "libgromacs${GMX_LIBS_SUFFIX}.pc"
+ COMPONENT development)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \defgroup module_analysisdata Parallelizable Handling of Output Data
+ * \ingroup group_analysismodules
+ * \brief
+ * Provides functionality for handling and processing output data from
+ * analysis.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+/*! \file
+ * \brief
+ * Public API convenience header for analysis data handling.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_H
+#define GMX_ANALYSISDATA_H
+
+#include "analysisdata/analysisdata.h"
+#include "analysisdata/arraydata.h"
+#include "analysisdata/modules/average.h"
+#include "analysisdata/modules/displacement.h"
+#include "analysisdata/modules/histogram.h"
+#include "analysisdata/modules/plot.h"
+
+#endif
--- /dev/null
+file(GLOB ANALYSISDATA_SOURCES *.cpp modules/*.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${ANALYSISDATA_SOURCES} PARENT_SCOPE)
+
+set(ANALYSISDATA_PUBLIC_HEADERS
+ abstractdata.h
+ analysisdata.h
+ arraydata.h
+ datamodule.h)
+install(FILES ${ANALYSISDATA_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/analysisdata
+ COMPONENT development)
+
+add_subdirectory(modules)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares internal implementation classes for gmx::AbstractAnalysisData and
+ * gmx::AbstractAnalysisDataStored.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ABSTRACTDATA_IMPL_H
+#define GMX_ANALYSISDATA_ABSTRACTDATA_IMPL_H
+
+#include <vector>
+
+#include "types/simple.h"
+#include "abstractdata.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Private implementation class for AbstractAnalysisData.
+ *
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisData::Impl
+{
+ public:
+ //! Shorthand for list of modules added to the data.
+ typedef std::vector<AnalysisDataModuleInterface *> ModuleList;
+
+ Impl();
+ ~Impl();
+
+ /*! \brief
+ * Present data already added to the data object to a module.
+ *
+ * \param[in] data Data object to read data from.
+ * \param[in] module Module to present the data to.
+ * \retval ::eeInvalidValue if \p module is not compatible with the
+ * data object.
+ * \retval ::eedataDataNotAvailable if all data is not available
+ * through getData().
+ *
+ * Uses getData() in \p data to access all data in the object, and
+ * calls the notification functions in \p module as if the module had
+ * been registered to the data object when the data was added.
+ */
+ int presentData(AbstractAnalysisData *data,
+ AnalysisDataModuleInterface *module);
+
+ //! List of modules added to the data.
+ ModuleList _modules;
+ //! Whether notifyDataStart() has been called.
+ bool _bDataStart;
+ //! Whether new data is being added.
+ bool _bInData;
+ //! Whether data for a frame is being added.
+ bool _bInFrame;
+ //! true if all modules support missing data.
+ bool _bAllowMissing;
+ //! x value for the current frame.
+ real _currx;
+ //! dx value for the current frame.
+ real _currdx;
+};
+
+/*! \internal \brief
+ * Internal implementation class for storing a single data frame.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataFrame
+{
+ public:
+ AnalysisDataFrame();
+ ~AnalysisDataFrame();
+
+ //! Allocate memory for a given number of columns.
+ void allocate(int ncol);
+
+ //! Zero-based global index of the frame.
+ int _index;
+ //! x value of the frame.
+ real _x;
+ //! Error of x for the frame.
+ real _dx;
+ //! Array of column values for the frame.
+ real *_y;
+ //! Array of column error values for the frame.
+ real *_dy;
+ //! Array of flags that tell whether a value is present.
+ bool *_present;
+};
+
+/*! \internal \brief
+ * Private implementation class for AbstractAnalysisDataStored.
+ *
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisDataStored::Impl
+{
+ public:
+ //! Shorthand for a list of data frames that are currently stored.
+ typedef std::vector<AnalysisDataFrame *> FrameList;
+
+ Impl();
+ ~Impl();
+
+ /*! \brief
+ * Calculates the index of a frame in the storage vector.
+ *
+ * \param[in] index Zero-based index for the frame to query.
+ * Negative value counts backwards from the current frame.
+ * \returns Index in \a _store corresponding to \p index,
+ * or -1 if not available.
+ */
+ int getStoreIndex(int index) const;
+
+ /*! \brief
+ * Total number of complete frames in the data.
+ */
+ int _nframes;
+ /*! \brief
+ * Number of elements in \a _store.
+ *
+ * Also holds the number of frames that should be stored, even before
+ * \a _store has been allocated.
+ */
+ int _nalloc;
+ //! Whether all frames should be stored.
+ bool _bStoreAll;
+ //! List of data frames that are currently stored.
+ FrameList _store;
+ /*! \brief
+ * Index in \a _store where the next frame will be stored.
+ *
+ * This counter is incremented after notifyPointsAdd() has been called
+ * for the frame.
+ */
+ int _nextind;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "gromacs/analysisdata/abstractdata.h"
+
+#include <cassert>
+
+// Legacy header.
+#include "smalloc.h"
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+#include "abstractdata-impl.h"
+#include "dataproxy.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AbstractAnalysisData::Impl
+ */
+
+AbstractAnalysisData::Impl::Impl()
+ : _bDataStart(false), _bInData(false), _bInFrame(false),
+ _bAllowMissing(true)
+{
+}
+
+AbstractAnalysisData::Impl::~Impl()
+{
+ ModuleList::const_iterator i;
+ for (i = _modules.begin(); i != _modules.end(); ++i)
+ {
+ delete *i;
+ }
+}
+
+
+int
+AbstractAnalysisData::Impl::presentData(AbstractAnalysisData *data,
+ AnalysisDataModuleInterface *module)
+{
+ int rc = module->dataStarted(data);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ bool bCheckMissing = _bAllowMissing
+ && !(module->flags() & AnalysisDataModuleInterface::efAllowMissing);
+ int ncol = data->columnCount();
+ for (int i = 0; i < data->frameCount(); ++i)
+ {
+ real x, dx;
+ const real *y, *dy;
+ const bool *present;
+
+ rc = data->getDataWErr(i, &x, &dx, &y, &dy, &present);
+ if (rc == 0)
+ {
+ if (bCheckMissing && present)
+ {
+ for (int j = 0; j < ncol; ++j)
+ {
+ if (!present[j])
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Missing data not supported by a module");
+ }
+ }
+ }
+ rc = module->frameStarted(x, dx);
+ }
+ if (rc == 0)
+ {
+ rc = module->pointsAdded(x, dx, 0, ncol, y, dy, present);
+ }
+ if (rc == 0)
+ {
+ rc = module->frameFinished();
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (!_bInData)
+ {
+ rc = module->dataFinished();
+ }
+ return rc;
+}
+
+
+/********************************************************************
+ * AbstractAnalysisData
+ */
+
+AbstractAnalysisData::AbstractAnalysisData()
+ : _impl(new Impl()), _ncol(0), _bMultiPoint(false)
+{
+}
+
+
+AbstractAnalysisData::~AbstractAnalysisData()
+{
+ delete _impl;
+}
+
+
+int
+AbstractAnalysisData::getData(int index, real *x, const real **y,
+ const bool **missing) const
+{
+ return getDataWErr(index, x, 0, y, 0, missing);
+}
+
+
+int
+AbstractAnalysisData::getErrors(int index, real *dx, const real **dy) const
+{
+ return getDataWErr(index, 0, dx, 0, dy, 0);
+}
+
+
+int
+AbstractAnalysisData::addModule(AnalysisDataModuleInterface *module)
+{
+ if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
+ || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
+ || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Data module not compatible with data object properties");
+ }
+
+ if (_impl->_bDataStart)
+ {
+ if (_impl->_bInFrame)
+ {
+ GMX_ERROR(eeInvalidCall,
+ "Cannot add data modules in mid-frame");
+ }
+ int rc = _impl->presentData(this, module);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (!(module->flags() & AnalysisDataModuleInterface::efAllowMissing))
+ {
+ _impl->_bAllowMissing = false;
+ }
+ _impl->_modules.push_back(module);
+ return 0;
+}
+
+
+int
+AbstractAnalysisData::addColumnModule(int col, int span,
+ AnalysisDataModuleInterface *module)
+{
+ assert(col >= 0 && span >= 1 && col + span <= _ncol);
+ if (_impl->_bDataStart)
+ {
+ GMX_ERROR(eeNotImplemented,
+ "Cannot add column modules after data");
+ }
+
+ AnalysisDataProxy *proxy = new AnalysisDataProxy(col, span, this);
+ int rc = proxy->addModule(module);
+ if (rc == 0)
+ {
+ rc = addModule(proxy);
+ }
+ if (rc != 0)
+ {
+ delete proxy;
+ }
+ return rc;
+}
+
+
+int
+AbstractAnalysisData::applyModule(AnalysisDataModuleInterface *module)
+{
+ if ((columnCount() > 1 && !(module->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
+ || (isMultipoint() && !(module->flags() & AnalysisDataModuleInterface::efAllowMultipoint))
+ || (!isMultipoint() && (module->flags() & AnalysisDataModuleInterface::efOnlyMultipoint)))
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Data module not compatible with data object properties");
+ }
+ if (!_impl->_bDataStart || _impl->_bInData)
+ {
+ GMX_ERROR(eeInvalidCall,
+ "Data module can only be applied to ready data");
+ }
+
+ return _impl->presentData(this, module);
+}
+
+
+void
+AbstractAnalysisData::setColumnCount(int ncol)
+{
+ assert(ncol > 0);
+ assert(_ncol == 0 || _impl->_modules.empty());
+ assert(!_impl->_bDataStart);
+ _ncol = ncol;
+}
+
+
+void
+AbstractAnalysisData::setMultipoint(bool multipoint)
+{
+ assert(_impl->_modules.empty());
+ assert(!_impl->_bDataStart);
+ _bMultiPoint = multipoint;
+}
+
+
+/*! \internal
+ * This method is not const because the dataStarted() methods of the attached
+ * modules can request storage of the data.
+ */
+int
+AbstractAnalysisData::notifyDataStart()
+{
+ assert(!_impl->_bDataStart);
+ assert(_ncol > 0);
+ _impl->_bDataStart = _impl->_bInData = true;
+
+ Impl::ModuleList::const_iterator i;
+
+ for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
+ {
+ if (_ncol > 1 && !((*i)->flags() & AnalysisDataModuleInterface::efAllowMulticolumn))
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Data module not compatible with data object properties");
+ }
+ int rc = (*i)->dataStarted(this);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisData::notifyFrameStart(real x, real dx) const
+{
+ assert(_impl->_bInData && !_impl->_bInFrame);
+ _impl->_bInFrame = true;
+ _impl->_currx = x;
+ _impl->_currdx = dx;
+
+ Impl::ModuleList::const_iterator i;
+ for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
+ {
+ int rc = (*i)->frameStarted(x, dx);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisData::notifyPointsAdd(int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present) const
+{
+ assert(_impl->_bInData && _impl->_bInFrame);
+ assert(firstcol >= 0 && n > 0 && firstcol + n <= _ncol);
+ if (present && !_impl->_bAllowMissing)
+ {
+ for (int i = 0; i < n; ++i)
+ {
+ if (!present[i])
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Missing data not supported by a module");
+ }
+ }
+ }
+
+ Impl::ModuleList::const_iterator i;
+ for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
+ {
+ int rc = (*i)->pointsAdded(_impl->_currx, _impl->_currdx, firstcol, n,
+ y, dy, present);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisData::notifyFrameFinish() const
+{
+ assert(_impl->_bInData && _impl->_bInFrame);
+ _impl->_bInFrame = false;
+
+ Impl::ModuleList::const_iterator i;
+
+ for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
+ {
+ int rc = (*i)->frameFinished();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisData::notifyDataFinish() const
+{
+ assert(_impl->_bInData && !_impl->_bInFrame);
+ _impl->_bInData = false;
+
+ Impl::ModuleList::const_iterator i;
+
+ for (i = _impl->_modules.begin(); i != _impl->_modules.end(); ++i)
+ {
+ int rc = (*i)->dataFinished();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * AnalysisDataFrame
+ */
+
+AnalysisDataFrame::AnalysisDataFrame()
+ : _y(NULL), _dy(NULL), _present(NULL)
+{
+}
+
+AnalysisDataFrame::~AnalysisDataFrame()
+{
+ sfree(_y);
+ sfree(_dy);
+ sfree(_present);
+}
+
+
+void AnalysisDataFrame::allocate(int ncol)
+{
+ snew(_y, ncol);
+ snew(_dy, ncol);
+ snew(_present, ncol);
+}
+
+
+/********************************************************************
+ * AbstractAnalysisDataStored::Impl
+ */
+
+AbstractAnalysisDataStored::Impl::Impl()
+ : _nframes(0), _nalloc(0), _bStoreAll(false), _nextind(-1)
+{
+}
+
+
+AbstractAnalysisDataStored::Impl::~Impl()
+{
+ FrameList::const_iterator i;
+ for (i = _store.begin(); i != _store.end(); ++i)
+ {
+ delete *i;
+ }
+}
+
+
+int
+AbstractAnalysisDataStored::Impl::getStoreIndex(int index) const
+{
+ // Check that the requested index is available.
+ if ((index < 0 && (-index > _nalloc || -index > _nframes))
+ || index >= _nframes || (index >= 0 && index < _nframes - _nalloc))
+ {
+ return -1;
+ }
+ // Calculate the index into the storage array.
+ if (index < 0)
+ {
+ index = _nextind + index;
+ if (index < 0)
+ {
+ index += _nalloc;
+ }
+ }
+ else if (_nframes > _nalloc)
+ {
+ index %= _nalloc;
+ }
+ return index;
+}
+
+
+/********************************************************************
+ * AbstractAnalysisDataStored
+ */
+
+AbstractAnalysisDataStored::AbstractAnalysisDataStored()
+ : _impl(new Impl())
+{
+}
+
+
+AbstractAnalysisDataStored::~AbstractAnalysisDataStored()
+{
+ delete _impl;
+}
+
+
+int
+AbstractAnalysisDataStored::frameCount() const
+{
+ return _impl->_nframes;
+}
+
+
+int
+AbstractAnalysisDataStored::getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present) const
+{
+ index = _impl->getStoreIndex(index);
+ if (index < 0)
+ {
+ return eedataDataNotAvailable;
+ }
+
+ // Retrieve the data.
+ AnalysisDataFrame *fr = _impl->_store[index];
+ if (x)
+ {
+ *x = fr->_x;
+ }
+ if (dx)
+ {
+ *dx = fr->_dx;
+ }
+ if (y)
+ {
+ *y = fr->_y;
+ }
+ if (dy)
+ {
+ *dy = fr->_dy;
+ }
+ if (present)
+ {
+ *present = fr->_present;
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisDataStored::requestStorage(int nframes)
+{
+ assert(nframes >= -1);
+ if (nframes == 0)
+ {
+ return 0;
+ }
+ if (isMultipoint())
+ {
+ GMX_ERROR(eeNotImplemented,
+ "Storage of multipoint data not supported");
+ }
+
+ // Handle the case when everything needs to be stored.
+ if (nframes == -1)
+ {
+ _impl->_bStoreAll = true;
+ _impl->_nalloc = 1;
+ return 0;
+ }
+ // Check whether an earier call has requested more storage.
+ if (_impl->_bStoreAll || nframes < _impl->_nalloc)
+ {
+ return 0;
+ }
+ _impl->_nalloc = nframes;
+ return 0;
+}
+
+
+void
+AbstractAnalysisDataStored::setMultipoint(bool multipoint)
+{
+ assert(_impl->_nalloc == 0 || !multipoint);
+ AbstractAnalysisData::setMultipoint(multipoint);
+}
+
+
+int
+AbstractAnalysisDataStored::startDataStore()
+{
+ // We first notify any attached modules, because they also might request
+ // some storage.
+ int rc = notifyDataStart();
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ int ncol = columnCount();
+
+ // If any storage has been requested, preallocate it.
+ if (_impl->_nalloc > 0)
+ {
+ _impl->_store.resize(_impl->_nalloc);
+ for (int i = 0; i < _impl->_nalloc; ++i)
+ {
+ _impl->_store[i] = new AnalysisDataFrame();
+ _impl->_store[i]->allocate(ncol);
+ }
+ _impl->_nextind = 0;
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisDataStored::startNextFrame(real x, real dx)
+{
+ // Start storing the frame if needed.
+ if (_impl->_nalloc > 0)
+ {
+ if (_impl->_nextind >= _impl->_nalloc)
+ {
+ if (_impl->_bStoreAll)
+ {
+ int ncol = columnCount();
+
+ _impl->_nalloc = _impl->_nextind + 1;
+ _impl->_store.resize(_impl->_nalloc);
+ for (int i = _impl->_nextind; i < _impl->_nalloc; ++i)
+ {
+ _impl->_store[i] = new AnalysisDataFrame();
+ _impl->_store[i]->allocate(ncol);
+ }
+ }
+ else
+ {
+ _impl->_nextind = 0;
+ }
+ }
+
+ _impl->_store[_impl->_nextind]->_x = x;
+ _impl->_store[_impl->_nextind]->_dx = dx;
+ }
+
+ // Notify any modules.
+ return notifyFrameStart(x, dx);
+}
+
+
+int
+AbstractAnalysisDataStored::storeThisFrame(const real *y, const real *dy,
+ const bool *present)
+{
+ int ncol = columnCount();
+
+ // Store the values if required.
+ if (_impl->_nextind >= 0)
+ {
+ AnalysisDataFrame *fr = _impl->_store[_impl->_nextind];
+
+ for (int i = 0; i < ncol; ++i)
+ {
+ fr->_y[i] = y[i];
+ if (dy)
+ {
+ fr->_dy[i] = dy[i];
+ }
+ if (present)
+ {
+ fr->_present[i] = present[i];
+ }
+ }
+ }
+ ++_impl->_nframes;
+
+ // Notify modules of new data.
+ int rc = notifyPointsAdd(0, ncol, y, dy, present);
+ // The index needs to be incremented after the notifications to allow
+ // the modules to use getData() properly.
+ if (_impl->_nextind >= 0)
+ {
+ ++_impl->_nextind;
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ return notifyFrameFinish();
+}
+
+
+int
+AbstractAnalysisDataStored::storeNextFrame(real x, real dx, const real *y,
+ const real *dy, const bool *present)
+{
+ int rc = startNextFrame(x, dx);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ return storeThisFrame(y, dy, present);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ABSTRACTDATA_H
+#define GMX_ANALYSISDATA_ABSTRACTDATA_H
+
+#include "../legacyheaders/types/simple.h"
+
+namespace gmx
+{
+
+//! Additional error codes for functions in the analysis data module.
+enum AnalysisDataErrorCode
+{
+ eedataDataNotAvailable = -1,
+};
+
+class AnalysisDataModuleInterface;
+
+/*! \libinternal \brief
+ * Abstract base class for all objects that provide data.
+ *
+ * The public interface includes functions for querying the data
+ * (isMultipoint(), columnCount(), frameCount(), getDataWErr(), getData(),
+ * getErrors(), requestStorage()) and functions for using modules for
+ * processing the data (addModule(), addColumnModule(), applyModule()).
+ *
+ * There are also protected functions for use in derived classes:
+ * setting the properties returned by isMultipoint() and columnCount()
+ * through setMultipoint() and setColumnCount(), and notifying attached
+ * modules of added data.
+ *
+ * Notice that even for non-const objects, the interface does not provide
+ * any means of altering the data. It is only possible to add modules,
+ * making it relatively safe to return a non-const pointer of this type
+ * pointing to an internal data structure without worrying about possible
+ * modifications of the data.
+ *
+ * It is up to subclasses to ensure that the protected functions are called
+ * in a correct sequence (the functions will assert in most incorrect use
+ * cases), and that the data provided through the public interface matches
+ * that passed to the modules with the notify methods.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisData
+{
+ public:
+ virtual ~AbstractAnalysisData();
+
+ /*! \brief
+ * Whether the data can have multiple points in the same column
+ * in the same frame.
+ *
+ * \returns \c true if multiple points in the same column are
+ * allowed within a single frame.
+ *
+ * This kind of data can appear in many histogramming applications
+ * (e.g., RDFs), where each trajectory frame has several data points
+ * (possibly a different number for each frame). The current interface
+ * doesn't support storing such data, but this should rarely be
+ * necessary.
+ *
+ * Derived classes can change the type by calling setMultipoint().
+ * If this is not done, the function always returns false.
+ */
+ bool isMultipoint() const { return _bMultiPoint; }
+ /*! \brief
+ * Returns the number of columns in the data.
+ *
+ * \returns The number of columns in the data.
+ *
+ * Derived classes should set the number of columns with
+ * setColumnCount().
+ * If the number of columns is not set, returns 0.
+ */
+ int columnCount() const { return _ncol; }
+ /*! \brief
+ * Returns the total number of frames in the data.
+ *
+ * \returns The total number of frames in the data.
+ *
+ * This function returns the number of frames that the object has
+ * produced. If requestStorage() has been successfully called,
+ * getData() can be used to access some or all of these frames.
+ */
+ virtual int frameCount() const = 0;
+ /*! \brief
+ * Access stored data.
+ *
+ * \param[in] index Frame index to access
+ * (negative indices count backwards from the current frame).
+ * \param[out] x
+ * \param[out] dx
+ * \param[out] y
+ * \param[out] dy
+ * \param[out] present Returns a pointer to an array that tells
+ * whether the corresponding column is present in that frame.
+ * If NULL, no missing information is returned.
+ * \retval 0 on success.
+ * \retval ::eedataDataNotAvailable if data for the requested frame is
+ * no longer available (this is regarded as a normal outcome,
+ * i.e., no error handlers are invoked).
+ *
+ * Derived classes can choose to return ::eedataDataNotAvailable if
+ * requestStorage() has not been called at all, or if the frame is
+ * too old (compared to the value given to requestStorage()).
+ */
+ virtual int getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present = 0) const = 0;
+ /*! \brief
+ * Convenience function for accessing stored data.
+ *
+ * \see getDataWErr()
+ */
+ int getData(int index, real *x, const real **y,
+ const bool **present = 0) const;
+ /*! \brief
+ * Convenience function for accessing errors for stored data.
+ *
+ * \see getDataWErr()
+ */
+ int getErrors(int index, real *dx, const real **dy) const;
+ /*! \brief
+ * Request storage of frames.
+ *
+ * \param[in] nframes Request storing at least \c nframes previous
+ * frames (-1 = request storing all).
+ * \retval 0 on success.
+ * \retval eedataInvalidCall if data has already been added and
+ * cannot be stored.
+ * \retval eedataNotSupported if the object does not support storage.
+ *
+ * If called multiple times, the largest request should be honored.
+ *
+ * \see getData()
+ */
+ virtual int requestStorage(int nframes = -1) = 0;
+
+ /*! \brief
+ * Adds a module to process the data.
+ *
+ * \param module Module to add.
+ * \retval 0 on success.
+ * \retval ::eeInvalidValue if \p module is not compatible with the
+ * data object.
+ * \retval ::eedataDataNotAvailable if data has already been added to
+ * the data object and everything is not available through
+ * getData().
+ *
+ * If data has already been added to the module, the new module
+ * immediately processes all existing data. ::eedataDataNotAvailable
+ * is returned if all data is not available through getData().
+ *
+ * If the call successful, the data object takes ownership of the
+ * module, and automatically destructs it when the data object itself
+ * is destroyed.
+ */
+ int addModule(AnalysisDataModuleInterface *module);
+ /*! \brief
+ * Adds a module that processes only a subset of the columns.
+ *
+ * \param[in] col First column.
+ * \param[in] span Number of columns.
+ * \param module Module to add.
+ * \retval 0 on success.
+ *
+ * Can return any of the return codes for addModule().
+ */
+ int addColumnModule(int col, int span, AnalysisDataModuleInterface *module);
+ /*! \brief
+ * Applies a module to process data that is ready.
+ *
+ * \param module Module to apply.
+ *
+ * This function works as addModule(), except that it does not take
+ * ownership of \p module. Also, it can only be called after the data
+ * is ready, and only if getData() gives access to all of the data.
+ * It is provided for additional flexibility in postprocessing
+ * in-memory data.
+ */
+ int applyModule(AnalysisDataModuleInterface *module);
+
+ protected:
+ AbstractAnalysisData();
+
+ /*! \brief
+ * Sets the number of columns.
+ *
+ * \param[in] ncol Number of columns in the data (must be > 0).
+ *
+ * Can be called only before notifyDataStart(), otherwise asserts.
+ * Multiple calls are only allowed if all of them occur before
+ * addModule() has been called, otherwise asserts (a single call
+ * can occur after addModule() if no calls have been made earlier).
+ *
+ * \see columnCount()
+ */
+ void setColumnCount(int ncol);
+ /*! \brief
+ * Sets whether the data has multiple points per column in a frame.
+ *
+ * \param[in] multipoint Whether multiple points per column are
+ * possible.
+ *
+ * Can be called only before addModule() or notifyDataStart(),
+ * otherwise asserts.
+ *
+ * \see isMultipoint()
+ */
+ void setMultipoint(bool multipoint);
+
+ /*! \brief
+ * Notifies attached modules of the start of data.
+ *
+ * Should be called once, after data properties have been set with
+ * setColumnCount() and isMultipoint(), and before any of the
+ * notification functions. The derived class should prepare for
+ * requestStorage() calls from the attached modules.
+ */
+ int notifyDataStart();
+ /*! \brief
+ * Notifies attached modules of the start of a frame.
+ *
+ * Should be called once for each frame, before notifyPointsAdd() calls
+ * for thet frame.
+ */
+ int notifyFrameStart(real x, real dx) const;
+ /*! \brief
+ * Notifies attached modules of the addition of points to the
+ * current frame.
+ *
+ * Can be called zero or more times for each frame.
+ * The caller should ensure that any column occurs at most once in the
+ * calls, unless the data is multipoint.
+ * For efficiency reasons, calls to this method should be aggregated
+ * whenever possible, i.e., it's better to handle multiple columns or
+ * even the whole frame in a single call rather than calling the method
+ * for each column separately.
+ */
+ int notifyPointsAdd(int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present) const;
+ /*! \brief
+ * Notifies attached modules of the end of a frame.
+ *
+ * Should be called once for each call of notifyFrameStart(), after any
+ * notifyPointsAdd() calls for the frame.
+ */
+ int notifyFrameFinish() const;
+ /*! \brief
+ * Notifies attached modules of the end of data.
+ *
+ * Should be called once, after all the other notification calls.
+ */
+ int notifyDataFinish() const;
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+ int _ncol;
+ bool _bMultiPoint;
+
+ // Disallow copy and assign.
+ AbstractAnalysisData(const AbstractAnalysisData &);
+ void operator =(const AbstractAnalysisData &);
+};
+
+
+/*! \libinternal \brief
+ * Abstract class that implements storage of data.
+ *
+ * This class implements a standard way of storing data, to avoid implementing
+ * storage in each derived class separately. All the pure virtual methods of
+ * AbstractData are implemented, and protected methods are provided to add data
+ * to the storage. These protected methods automatically call notifyDataStart(),
+ * notifyFrameStart(), notifyPointsAdd() and notifyFrameFinish()
+ * functions in AbstractAnalysisData, but the derived class should still
+ * call notifyDataFinish().
+ *
+ * Additional protected functions could be implemented to allow optimization:
+ * in the current interface, some data copying is unavoidable.
+ * Some changes could make it possible to obtain a pointer to the
+ * storage, allowing the calculated values to be stored there directly
+ * instead of a temporary array.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisDataStored : public AbstractAnalysisData
+{
+ public:
+ virtual ~AbstractAnalysisDataStored();
+
+ virtual int frameCount() const;
+ virtual int getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present = 0) const;
+ virtual int requestStorage(int nframes = -1);
+
+ protected:
+ AbstractAnalysisDataStored();
+
+ /*! \copydoc AbstractAnalysisData::setMultipoint()
+ *
+ * The overridden method also asserts if
+ * storage has been requested and \p multipoint is \c true.
+ */
+ void setMultipoint(bool multipoint);
+
+ //! Start storing data.
+ int startDataStore();
+ //! Starts storing a next frame.
+ int startNextFrame(real x, real dx);
+ //! Stores the whole frame in a single call after start_next_frame().
+ int storeThisFrame(const real *y, const real *dy, const bool *present);
+ //! Convenience function for storing a whole frame in a single call.
+ int storeNextFrame(real x, real dx, const real *y, const real *dy,
+ const bool *present);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Copy and assign disallowed by base class.
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::AnalysisData.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ANALYSISDATA_IMPL_H
+#define GMX_ANALYSISDATA_ANALYSISDATA_IMPL_H
+
+#include <vector>
+
+#include "analysisdata.h"
+
+#include "abstractdata-impl.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Private implementation class for AnalysisData.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisData::Impl
+{
+ public:
+ //! Shorthand for a list of data handles.
+ typedef std::vector<AnalysisDataHandle *> HandleList;
+ //! Shorthand for a list of frames.
+ typedef std::vector<AnalysisDataFrame *> FrameList;
+
+ //! Creates an implementation class associated with the given object.
+ explicit Impl(AnalysisData *data);
+ ~Impl();
+
+ /*! \brief
+ * Handles a new frame.
+ *
+ * If all earlier frames are ready, the data is directly passed to
+ * AbstractStoredData. Otherwise, it is put into the correct location
+ * in \a _pending. Calls processPendingFrame() after processing \p fr.
+ */
+ int addPendingFrame(AnalysisDataFrame *fr);
+ /*! \brief
+ * Passes pending frames to base class if all earlier frames are ready.
+ */
+ int processPendingFrames();
+ //! Increments \a _pstart.
+ void incrementPStart();
+
+ //! The data object that uses this implementation class.
+ AnalysisData &_data;
+ //! List of handles for this data object.
+ HandleList _handles;
+ /*! \brief
+ * Index into \a _pending that points to the location where the current
+ * frame would go.
+ */
+ size_t _pstart;
+ /*! \brief
+ * Circular buffer for frames that are ready but waiting for earlier
+ * frames.
+ */
+ FrameList _pending;
+};
+
+/*! \internal \brief
+ * Private implementation class for AnalysisDataHandle.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataHandle::Impl
+{
+ public:
+ //! Creates a handle associated with the given data object.
+ explicit Impl(AnalysisData *data);
+ ~Impl();
+
+ //! The data object that this handle belongs to.
+ AnalysisData &_data;
+ //! Frame object where the current frame is being accumulated.
+ AnalysisDataFrame *_frame;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in analysisdata.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "gromacs/analysisdata/analysisdata.h"
+
+#include <algorithm>
+
+#include <cassert>
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+#include "abstractdata-impl.h"
+#include "analysisdata-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AnalysisData::Impl
+ ********************************************************************/
+
+static bool
+frame_index_gtr(AnalysisDataFrame *a, AnalysisDataFrame *b)
+{
+ return a->_index > b->_index;
+}
+
+
+AnalysisData::Impl::Impl(AnalysisData *data)
+ : _data(*data), _pstart(0)
+{
+}
+
+
+AnalysisData::Impl::~Impl()
+{
+ HandleList::const_iterator i;
+ for (i = _handles.begin(); i != _handles.end(); ++i)
+ {
+ delete *i;
+ }
+
+ FrameList::const_iterator j;
+ for (j = _pending.begin(); j != _pending.end(); ++j)
+ {
+ delete *j;
+ }
+}
+
+
+int
+AnalysisData::Impl::addPendingFrame(AnalysisDataFrame *fr)
+{
+ assert(fr->_index >= _data.frameCount());
+ size_t pindex = fr->_index - _data.frameCount();
+ if (pindex == 0)
+ {
+ int rc;
+
+ // Just store our frame if it is the next one.
+ rc = _data.storeNextFrame(fr->_x, fr->_dx,
+ fr->_y, fr->_dy, fr->_present);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ incrementPStart();
+ }
+ else
+ {
+ if (pindex >= _pending.size())
+ {
+ // TODO: We need to wait until earlier frames are ready...
+ }
+ // TODO: This is not thread-safe.
+ pindex += _pstart;
+ if (pindex > _pending.size())
+ {
+ pindex -= _pending.size();
+ }
+
+ int ncol = _data.columnCount();
+ _pending[pindex]->_x = fr->_x;
+ _pending[pindex]->_dx = fr->_dx;
+ for (int i = 0; i < ncol; ++i)
+ {
+ _pending[pindex]->_y[i] = fr->_y[i];
+ _pending[pindex]->_dy[i] = fr->_dy[i];
+ _pending[pindex]->_present[i] = fr->_present[i];
+ }
+ _pending[pindex]->_index = fr->_index;
+ }
+ return processPendingFrames();
+}
+
+
+int
+AnalysisData::Impl::processPendingFrames()
+{
+ while (_pending[_pstart]->_index != -1)
+ {
+ AnalysisDataFrame *fr = _pending[_pstart];
+
+ int rc = _data.storeNextFrame(fr->_x, fr->_dx,
+ fr->_y, fr->_dy, fr->_present);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ fr->_index = -1;
+ incrementPStart();
+ }
+ return 0;
+}
+
+
+void
+AnalysisData::Impl::incrementPStart()
+{
+ size_t val = _pstart;
+
+ ++val;
+ if (val >= _pending.size())
+ {
+ val -= _pending.size();
+ }
+ _pstart = val;
+}
+
+
+/********************************************************************
+ * AnalysisData
+ */
+
+AnalysisData::AnalysisData()
+ : _impl(new Impl(this))
+{
+}
+
+
+AnalysisData::~AnalysisData()
+{
+ delete _impl;
+}
+
+
+void
+AnalysisData::setColumns(int ncol, bool multipoint)
+{
+ assert(ncol > 0);
+ assert(_impl->_handles.empty());
+ setColumnCount(ncol);
+ setMultipoint(multipoint);
+}
+
+
+int
+AnalysisData::startData(AnalysisDataHandle **handlep,
+ AnalysisDataParallelOptions opt)
+{
+ if (_impl->_handles.empty())
+ {
+ int rc = startDataStore();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ else if (isMultipoint())
+ {
+ GMX_ERROR(eeNotImplemented,
+ "Parallelism not supported for multipoint data");
+ }
+
+ AnalysisDataHandle *handle = new AnalysisDataHandle(this);
+ _impl->_handles.push_back(handle);
+ *handlep = handle;
+
+ _impl->_pending.resize(2 * _impl->_handles.size() - 1);
+ Impl::FrameList::iterator i;
+ for (i = _impl->_pending.begin(); i != _impl->_pending.end(); ++i)
+ {
+ *i = new AnalysisDataFrame();
+ (*i)->allocate(columnCount());
+ (*i)->_index = -1;
+ }
+
+ return 0;
+}
+
+
+int
+AnalysisData::finishData(AnalysisDataHandle *handle)
+{
+ Impl::HandleList::iterator i;
+
+ i = std::find(_impl->_handles.begin(), _impl->_handles.end(), handle);
+ assert(i != _impl->_handles.end());
+
+ _impl->_handles.erase(i);
+ delete handle;
+
+ if (_impl->_handles.empty())
+ {
+ return notifyDataFinish();
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * AnalysisDataHandle::Impl
+ */
+
+AnalysisDataHandle::Impl::Impl(AnalysisData *data)
+ : _data(*data), _frame(NULL)
+{
+ if (!_data.isMultipoint())
+ {
+ _frame = new AnalysisDataFrame();
+ _frame->allocate(_data.columnCount());
+ }
+}
+
+
+AnalysisDataHandle::Impl::~Impl()
+{
+ delete _frame;
+}
+
+
+/********************************************************************
+ * AnalysisDataHandle
+ */
+
+AnalysisDataHandle::AnalysisDataHandle(AnalysisData *data)
+ : _impl(new Impl(data))
+{
+}
+
+
+AnalysisDataHandle::~AnalysisDataHandle()
+{
+ delete _impl;
+}
+
+
+int
+AnalysisDataHandle::startFrame(int index, real x, real dx)
+{
+ if (_impl->_data.isMultipoint())
+ {
+ return _impl->_data.notifyFrameStart(x, dx);
+ }
+ else
+ {
+ _impl->_frame->_index = index;
+ _impl->_frame->_x = x;
+ _impl->_frame->_dx = dx;
+ for (int i = 0; i < _impl->_data.columnCount(); ++i)
+ {
+ _impl->_frame->_y[i] = 0.0;
+ _impl->_frame->_dy[i] = 0.0;
+ _impl->_frame->_present[i] = false;
+ }
+ return 0;
+ }
+}
+
+
+int
+AnalysisDataHandle::addPoint(int col, real y, real dy, bool present)
+{
+ if (_impl->_data.isMultipoint())
+ {
+ return _impl->_data.notifyPointsAdd(col, 1, &y, &dy, &present);
+ }
+ else
+ {
+ assert(!_impl->_frame->_present[col]);
+ _impl->_frame->_y[col] = y;
+ _impl->_frame->_dy[col] = dy;
+ _impl->_frame->_present[col] = present;
+ return 0;
+ }
+}
+
+
+int
+AnalysisDataHandle::addPoints(int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ if (_impl->_data.isMultipoint())
+ {
+ return _impl->_data.notifyPointsAdd(firstcol, n, y, dy, present);
+ }
+ else
+ {
+ for (int i = 0; i < n; ++i)
+ {
+ addPoint(firstcol + i, y[i], dy ? dy[i] : 0.0,
+ present ? present[i] : true);
+ }
+ return 0;
+ }
+}
+
+
+int
+AnalysisDataHandle::finishFrame()
+{
+ if (_impl->_data.isMultipoint())
+ {
+ return _impl->_data.notifyFrameFinish();
+ }
+ else
+ {
+ return _impl->_data._impl->addPendingFrame(_impl->_frame);
+ }
+}
+
+
+int
+AnalysisDataHandle::addFrame(int index, real x, const real *y, const real *dy,
+ const bool *present)
+{
+ return addFrame(index, x, 0.0, y, dy, present);
+}
+
+
+int
+AnalysisDataHandle::addFrame(int index, real x, real dx,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ int rc = startFrame(index, x, dx);
+ if (rc == 0)
+ {
+ rc = addPoints(0, _impl->_data.columnCount(), y, dy, present);
+ }
+ if (rc == 0)
+ {
+ rc = finishFrame();
+ }
+ return rc;
+}
+
+
+int
+AnalysisDataHandle::finishData()
+{
+ // Calls delete this
+ return _impl->_data.finishData(this);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisData and related classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ANALYSISDATA_H
+#define GMX_ANALYSISDATA_ANALYSISDATA_H
+
+#include "abstractdata.h"
+
+namespace gmx
+{
+
+class AnalysisDataHandle;
+
+//! Placeholder type for parallelization options.
+typedef void *AnalysisDataParallelOptions;
+
+/*! \brief
+ * Parallelizable data container for raw data.
+ *
+ * This is the only data object (in addition to the tightly coupled
+ * \c AnalysisDataHandle object) that needs explicit parallelization.
+ *
+ * Special note for MPI implementation: assuming that the initialization of
+ * data objects is identical in all processes, associating the data objects
+ * in different MPI processes should be possible without changes in the
+ * interface.
+ * Alternative, more robust implementation could get a unique ID as parameter
+ * to the constructor or a separate function, but would require all tools to
+ * provide it.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisData : public AbstractAnalysisDataStored
+{
+ public:
+ //! Creates an empty analysis data object.
+ AnalysisData();
+ virtual ~AnalysisData();
+
+ /*! \brief
+ * Sets the number of columns in the data and type of the data.
+ *
+ * \see isMultipoint()
+ */
+ void setColumns(int ncol, bool multipoint = false);
+
+ /*! \brief
+ * Create a handle for adding data.
+ *
+ * \param[out] handlep The created handle is stored in \p *handlep.
+ * \param[in] opt Options for setting how this handle will be
+ * used.
+ */
+ int startData(AnalysisDataHandle **handlep,
+ AnalysisDataParallelOptions opt);
+ /*! \brief
+ * Destroy a handle after all data has been added.
+ *
+ * \param[in] handle Handle to destroy.
+ *
+ * The pointer \p handle is invalid after the call.
+ */
+ int finishData(AnalysisDataHandle *handle);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ friend class Impl;
+ friend class AnalysisDataHandle;
+
+ // Copy and assign disallowed by base class.
+};
+
+
+/*! \brief
+ * Handle for inserting data into AnalysisData.
+ *
+ * Several handles can exist concurrently.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataHandle
+{
+ public:
+ //! Start data for a new frame.
+ int startFrame(int index, real x, real dx = 0.0);
+ //! Add a data point for in single column for the current frame.
+ int addPoint(int col, real y, real dy = 0.0, bool present = true);
+ //! Add multiple data points in neighboring columns for the current frame.
+ int addPoints(int firstcol, int n,
+ const real *y, const real *dy = 0,
+ const bool *present = 0);
+ //! Finish data for the current frame.
+ int finishFrame();
+ //! Convenience function for adding a complete frame.
+ int addFrame(int index, real x, const real *y, const real *dy = 0,
+ const bool *present = 0);
+ //! Convenience function for adding a complete frame.
+ int addFrame(int index, real x, real dx,
+ const real *y, const real *dy = 0,
+ const bool *present = 0);
+ //! Calls AnalysisData::finishData() for this handle.
+ int finishData();
+
+ private:
+ /*! \brief
+ * Creates a new data handle associated with \p data.
+ *
+ * \param data Data to associate the handle with.
+ *
+ * The constructor is private because data handles should only be
+ * constructed through AnalysisData::startData().
+ */
+ explicit AnalysisDataHandle(AnalysisData *data);
+ /*! \brief
+ * Frees memory allocated for the internal implementation.
+ *
+ * The destructor is private because data handles should only be
+ * deleted by calling AnalysisData::finishData() (or finishData()).
+ */
+ ~AnalysisDataHandle();
+
+ class Impl;
+
+ Impl *_impl;
+
+ friend class AnalysisData;
+
+ // Disallow copy and assign.
+ AnalysisDataHandle(const AnalysisDataHandle &);
+ void operator =(const AnalysisDataHandle &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in arraydata.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "gromacs/analysisdata/arraydata.h"
+
+#include <cassert>
+
+// Legacy header.
+#include "smalloc.h"
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+namespace gmx
+{
+
+AbstractAnalysisArrayData::AbstractAnalysisArrayData()
+ : _nrows(0), _value(NULL), _xstart(0.0), _xstep(1.0), _bReady(false)
+{
+}
+
+AbstractAnalysisArrayData::~AbstractAnalysisArrayData()
+{
+ sfree(_value);
+}
+
+
+int
+AbstractAnalysisArrayData::frameCount() const
+{
+ return _bReady ? _nrows : 0;
+}
+
+
+int
+AbstractAnalysisArrayData::getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present) const
+{
+ if (index < 0)
+ {
+ index += _nrows;
+ if (index < 0)
+ {
+ GMX_ERROR(eeInvalidValue, "Frame index out of range");
+ }
+ }
+ if (index >= frameCount())
+ {
+ GMX_ERROR(eeInvalidValue, "Frame index out of range");
+ }
+ if (x)
+ {
+ *x = _xstart + index * _xstep;
+ }
+ if (dx)
+ {
+ *dx = 0.0;
+ }
+ if (y)
+ {
+ *y = _value + (index * columnCount());
+ }
+ if (dy)
+ {
+ // TODO: Implement
+ *dy = NULL;
+ }
+ if (present)
+ {
+ // TODO: Implement
+ *present = NULL;
+ }
+ return 0;
+}
+
+
+int
+AbstractAnalysisArrayData::requestStorage(int nframes)
+{
+ return 0;
+}
+
+
+void
+AbstractAnalysisArrayData::setColumnCount(int ncols)
+{
+ assert(!_value);
+ AbstractAnalysisData::setColumnCount(ncols);
+}
+
+
+void
+AbstractAnalysisArrayData::setRowCount(int nrows)
+{
+ assert(nrows > 0);
+ assert(!_value && columnCount() > 0);
+ _nrows = nrows;
+ snew(_value, _nrows * columnCount());
+}
+
+
+void
+AbstractAnalysisArrayData::setXAxis(real start, real step)
+{
+ assert(!_bReady);
+ _xstart = start;
+ _xstep = step;
+}
+
+
+int
+AbstractAnalysisArrayData::valuesReady()
+{
+ assert(columnCount() > 0 && _nrows > 0);
+ if (_bReady)
+ {
+ return 0;
+ }
+ _bReady = true;
+
+ int rc = notifyDataStart();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ for (int i = 0; i < _nrows; ++i)
+ {
+ rc = notifyFrameStart(_xstart + i * _xstep, 0);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = notifyPointsAdd(0, columnCount(), _value + (i * columnCount()),
+ NULL, NULL);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = notifyFrameFinish();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return notifyDataFinish();
+}
+
+
+void
+AbstractAnalysisArrayData::copyContents(const AbstractAnalysisArrayData *src,
+ AbstractAnalysisArrayData *dest)
+{
+ assert(src->columnCount() > 0 && src->_nrows > 0 && src->_value);
+ assert(!dest->_value);
+ dest->setColumnCount(src->columnCount());
+ dest->setRowCount(src->_nrows);
+ dest->setXAxis(src->_xstart, src->_xstep);
+ for (int i = 0; i < src->_nrows * src->columnCount(); ++i)
+ {
+ dest->_value[i] = src->_value[i];
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AbstractAnalysisArrayData and gmx::AnalysisArrayData.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ARRAYDATA_H
+#define GMX_ANALYSISDATA_ARRAYDATA_H
+
+#include <cassert>
+
+#include "abstractdata.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Abstract base class for data objects that present in-memory data.
+ *
+ * This class implements a subclass of AbstractAnalysisData that presents an
+ * in-memory array through the AbstractAnalysisData interface. Subclasses
+ * should initialize the in-memory array through the provided protected member
+ * functions.
+ *
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisArrayData : public AbstractAnalysisData
+{
+ public:
+ virtual ~AbstractAnalysisArrayData();
+
+ virtual int frameCount() const;
+ virtual int getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present = 0) const;
+ virtual int requestStorage(int nframes = -1);
+
+ protected:
+ AbstractAnalysisArrayData();
+
+ /*! \brief
+ * Returns the number of rows in the data array.
+ *
+ * This function is identical to frameCount(), except that frameCount()
+ * returns 0 before valuesReady() has been called.
+ */
+ int rowCount() const { return _nrows; }
+ /*! \brief
+ * Sets the number of columns in the data array.
+ */
+ void setColumnCount(int ncols);
+ /*! \brief
+ * Sets the number of rows in the data array.
+ */
+ void setRowCount(int nrows);
+ //! Returns the x value of the first frame.
+ real xstart() const { return _xstart; }
+ //! Returns the step between frame x values.
+ real xstep() const { return _xstep; }
+ /*! \brief
+ * Sets the values reported as x values for frames.
+ */
+ void setXAxis(real start, real step);
+ //! Returns a reference to a given array element.
+ real &value(int row, int col)
+ {
+ assert(row >= 0 && row < _nrows);
+ assert(col >= 0 && col < columnCount());
+ assert(_value);
+ return _value[row * columnCount() + col];
+ }
+ //! Returns a given array element.
+ const real &value(int row, int col) const
+ {
+ assert(row >= 0 && row < _nrows);
+ assert(col >= 0 && col < columnCount());
+ assert(_value);
+ return _value[row * columnCount() + col];
+ }
+ /*! \brief
+ * Sets the value of an element in the array.
+ */
+ void setValue(int row, int col, real val)
+ {
+ value(row, col) = val;
+ }
+ /*! \brief
+ * Notifies modules of the data.
+ *
+ * This function should be called once the values in the array
+ * have been initialized. The values should not be changed after this
+ * function has been called.
+ */
+ int valuesReady();
+
+ /*! \brief
+ * Copies the contents into a new object.
+ *
+ * \param[in] src Object to copy data from.
+ * \param[in,out] dest Empty array data object to copy data to.
+ *
+ * \p dest should not have previous contents.
+ */
+ static void copyContents(const AbstractAnalysisArrayData *src,
+ AbstractAnalysisArrayData *dest);
+
+ private:
+ int _nrows;
+ real *_value;
+ real _xstart;
+ real _xstep;
+ bool _bReady;
+
+ // Copy and assign disallowed by base.
+};
+
+/*! \brief
+ * Simple in-memory data array.
+ *
+ * This class simply exposes the protected functions of
+ * AbstractAnalysisArrayData to allow construction of simple in-memory data
+ * arrays for input into data modules.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisArrayData : public AbstractAnalysisArrayData
+{
+ public:
+ AnalysisArrayData() {}
+
+ using AbstractAnalysisArrayData::rowCount;
+ using AbstractAnalysisArrayData::setColumnCount;
+ using AbstractAnalysisArrayData::setRowCount;
+ using AbstractAnalysisArrayData::xstart;
+ using AbstractAnalysisArrayData::xstep;
+ using AbstractAnalysisArrayData::setXAxis;
+ using AbstractAnalysisArrayData::value;
+ using AbstractAnalysisArrayData::setValue;
+ using AbstractAnalysisArrayData::valuesReady;
+
+ // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AnalysisDataModuleInterface.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATAMODULE_H
+#define GMX_ANALYSISDATA_DATAMODULE_H
+
+#include "../legacyheaders/types/simple.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+
+/*! \brief
+ * Interface for a module that gets notified whenever data is added.
+ *
+ * The interface provides one method (flags()) that describes features of
+ * data objects the module supports. Only most common features are included
+ * in the flags; custom checks can be implemented in the dataStarted() method
+ * (see below).
+ * All other methods in the interface are callbacks that are called by the
+ * data object to which the module is attached to describe the data.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataModuleInterface
+{
+ public:
+ /*! \brief
+ * Possible flags for flags().
+ */
+ enum {
+ //! The module can process multipoint data.
+ efAllowMultipoint = 0x01,
+ //! The module does not make sense for non-multipoint data.
+ efOnlyMultipoint = 0x02,
+ //! The module can process data with more than one column.
+ efAllowMulticolumn = 0x04,
+ //! The module can process data with missing points.
+ efAllowMissing = 0x08,
+ };
+
+ virtual ~AnalysisDataModuleInterface() {};
+
+ /*! \brief
+ * Returns properties supported by the module.
+ *
+ * The return value of this method should not change after the module
+ * has been added to a data (this responsibility can, and in most cases
+ * must, be delegated to the user of the module).
+ *
+ * The purpose of this method is to remove the need for common checks
+ * for data compatibility in the classes that implement the interface.
+ * Instead, AbstractData performs these checks based on the flags
+ * provided.
+ */
+ virtual int flags() const = 0;
+
+ /*! \brief
+ * Called (once) when the data has been set up properly.
+ *
+ * The data to which the module is attached is passed as an argument
+ * to provide access to properties of the data for initialization
+ * and/or validation.
+ * This is the only place where the module gets access to the data;
+ * if properties of the data are required later, the module should
+ * store them internally. It is guaranteed that the data properties
+ * (column count, whether it's multipoint) do not change once this
+ * method has been called.
+ */
+ virtual int dataStarted(AbstractAnalysisData *data) = 0;
+ /*! \brief
+ * Called at the start of each data frame.
+ */
+ virtual int frameStarted(real x, real dx) = 0;
+ /*! \brief
+ * Called one or more times during each data frame.
+ *
+ * For convenience, the \p x and \p dx values for the frame are
+ * passed to each call of this function.
+ */
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present) = 0;
+ /*! \brief
+ * Called when a data frame is finished.
+ */
+ virtual int frameFinished() = 0;
+ /*! \brief
+ * Called (once) when no more data is available.
+ */
+ virtual int dataFinished() = 0;
+
+ protected:
+ AnalysisDataModuleInterface() {}
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::AnalysisDataProxy.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "dataproxy.h"
+
+#include <cassert>
+
+namespace gmx
+{
+
+AnalysisDataProxy::AnalysisDataProxy(int col, int span,
+ AbstractAnalysisData *data)
+ : _source(*data), _col(col), _span(span)
+{
+ assert(data);
+ assert(col >= 0 && span > 0);
+ setColumnCount(span);
+ setMultipoint(_source.isMultipoint());
+}
+
+
+int
+AnalysisDataProxy::frameCount() const
+{
+ return _source.frameCount();
+}
+
+
+int
+AnalysisDataProxy::getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **missing) const
+{
+ int rc = _source.getDataWErr(index, x, dx, y, dy, missing);
+ if (rc == 0)
+ {
+ if (y && *y)
+ {
+ *y += _col;
+ }
+ if (dy && *dy)
+ {
+ *dy += _col;
+ }
+ if (missing && *missing)
+ {
+ *missing += _col;
+ }
+ }
+ return rc;
+}
+
+
+int
+AnalysisDataProxy::requestStorage(int nframes)
+{
+ return _source.requestStorage(nframes);
+}
+
+
+int
+AnalysisDataProxy::flags() const
+{
+ return efAllowMultipoint | efAllowMulticolumn | efAllowMissing;
+}
+
+
+int
+AnalysisDataProxy::dataStarted(AbstractAnalysisData *data)
+{
+ assert(data == &_source);
+ assert(_col + _span <= _source.columnCount());
+ return notifyDataStart();
+}
+
+
+int
+AnalysisDataProxy::frameStarted(real x, real dx)
+{
+ return notifyFrameStart(x, dx);
+}
+
+
+int
+AnalysisDataProxy::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *missing)
+{
+ if (firstcol + n <= _col || firstcol >= _col + _span)
+ {
+ return 0;
+ }
+ firstcol -= _col;
+ if (firstcol < 0)
+ {
+ if (y)
+ {
+ y += -firstcol;
+ }
+ if (dy)
+ {
+ dy += -firstcol;
+ }
+ if (missing)
+ {
+ missing += -firstcol;
+ }
+ n -= -firstcol;
+ firstcol = 0;
+ }
+ if (firstcol + n > _span)
+ {
+ n = _span - firstcol;
+ }
+ return notifyPointsAdd(firstcol, n, y, dy, missing);
+}
+
+
+int
+AnalysisDataProxy::frameFinished()
+{
+ return notifyFrameFinish();
+}
+
+
+int
+AnalysisDataProxy::dataFinished()
+{
+ return notifyDataFinish();
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::AnalysisDataProxy.
+ *
+ * This header is only meant for internal use of the gmx::AbstractAnalysisData
+ * class to implement modules that handle only a subset of columns.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATAPROXY_H
+#define GMX_ANALYSISDATA_DATAPROXY_H
+
+#include "abstractdata.h"
+#include "datamodule.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Internal implementation class used to implement column modules.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataProxy : public AbstractAnalysisData,
+ public AnalysisDataModuleInterface
+{
+ public:
+ /*! \brief
+ * Creates a proxy object that only presents certain columns.
+ *
+ * \param[in] col First column to present.
+ * \param[in] span Number of columns to present.
+ * \param[in] data Data object that should be wrapped.
+ */
+ AnalysisDataProxy(int col, int span, AbstractAnalysisData *data);
+
+ virtual int frameCount() const;
+ virtual int getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **missing = 0) const;
+ virtual int requestStorage(int nframes = -1);
+
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *missing);
+ virtual int frameFinished();
+ virtual int dataFinished();
+
+ private:
+ AbstractAnalysisData &_source;
+ int _col;
+ int _span;
+
+ // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+set(ANALYSISDATA_MODULES_PUBLIC_HEADERS
+ average.h
+ displacement.h
+ histogram.h
+ plot.h)
+install(FILES ${ANALYSISDATA_MODULES_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/analysisdata/modules
+ COMPONENT development)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::AnalysisDataAverageModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "gromacs/analysisdata/modules/average.h"
+
+#include <cmath>
+
+// Legacy include
+#include "smalloc.h"
+
+#include "gromacs/basicmath.h"
+
+namespace gmx
+{
+
+AnalysisDataAverageModule::AnalysisDataAverageModule()
+ : _nsamples(NULL)
+{
+ setColumnCount(2);
+}
+
+
+AnalysisDataAverageModule::~AnalysisDataAverageModule()
+{
+ sfree(_nsamples);
+}
+
+
+int
+AnalysisDataAverageModule::flags() const
+{
+ return efAllowMultipoint | efAllowMulticolumn | efAllowMissing;
+}
+
+
+int
+AnalysisDataAverageModule::dataStarted(AbstractAnalysisData *data)
+{
+ int nrows = data->columnCount();
+ setRowCount(nrows);
+ snew(_nsamples, nrows);
+ return 0;
+}
+
+
+int
+AnalysisDataAverageModule::frameStarted(real x, real dx)
+{
+ return 0;
+}
+
+
+int
+AnalysisDataAverageModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ if (!present || present[i])
+ {
+ value(firstcol + i, 0) += y[i];
+ value(firstcol + i, 1) += y[i] * y[i];
+ _nsamples[firstcol + i] += 1;
+ }
+ }
+ return 0;
+}
+
+
+int
+AnalysisDataAverageModule::frameFinished()
+{
+ return 0;
+}
+
+
+int
+AnalysisDataAverageModule::dataFinished()
+{
+ for (int i = 0; i < rowCount(); ++i)
+ {
+ real ave = value(i, 0) / _nsamples[i];
+ real std = sqrt(value(i, 1) / _nsamples[i] - ave * ave);
+ setValue(i, 0, ave);
+ setValue(i, 1, std);
+ }
+ return valuesReady();
+}
+
+
+real
+AnalysisDataAverageModule::average(int index) const
+{
+ return value(index, 0);
+}
+
+
+real
+AnalysisDataAverageModule::stddev(int index) const
+{
+ return value(index, 1);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataAverageModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_AVERAGE_H
+#define GMX_ANALYSISDATA_MODULES_AVERAGE_H
+
+#include "../arraydata.h"
+#include "../datamodule.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Data module for simple averaging of columns.
+ *
+ * Output data contains a frame for each column of input data.
+ * There are two columns: the average and standard deviation of
+ * that column.
+ * The data becomes available only after the original data has been
+ * finished.
+ *
+ * Multipoint data and missing data points are both supported. The average
+ * is always calculated over all data points present in a column.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataAverageModule : public AbstractAnalysisArrayData,
+ public AnalysisDataModuleInterface
+{
+ public:
+ AnalysisDataAverageModule();
+ virtual ~AnalysisDataAverageModule();
+
+ using AbstractAnalysisArrayData::setXAxis;
+
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+ virtual int frameFinished();
+ virtual int dataFinished();
+
+ //! Convenience access to the average of a data column.
+ real average(int index) const;
+ //! Convenience access to the standard deviation of a data column.
+ real stddev(int index) const;
+
+ private:
+ int *_nsamples;
+
+ // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for
+ * gmx::AnalysisDataDisplacementModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_DISPLACEMENT_IMPL_H
+#define GMX_ANALYSISDATA_MODULES_DISPLACEMENT_IMPL_H
+
+#include "displacement.h"
+
+namespace gmx
+{
+
+class AbstractHistogramModule;
+
+/*! \internal \brief
+ * Private implementation class for AnalysisDataDisplacementModule.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataDisplacementModule::Impl
+{
+ public:
+ Impl();
+ ~Impl();
+
+ //! Maximum number of particles for which the displacements are calculated.
+ int nmax;
+ //! Maximum time for which the displacements are needed.
+ real tmax;
+ //! Number of dimensions per data point.
+ int ndim;
+
+ //! TRUE if no frames have been read.
+ bool bFirst;
+ //! Stores the time of the first frame.
+ real t0;
+ //! Stores the time interval between frames.
+ real dt;
+ //! Stores the time of the current frame.
+ real t;
+ //! Stores the index in the store for the current positions.
+ int ci;
+
+ //! Maximum number of positions to store for a particle.
+ int max_store;
+ //! The total number of positions ever stored (can be larger than \p max_store).
+ int nstored;
+ //! Old values.
+ real *oldval;
+ //! The most recently calculated displacements.
+ real *currd;
+
+ //! Histogram module for calculating MSD histograms, or NULL if not set.
+ AbstractHistogramModule *histm;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::AnalysisDataDisplacementModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "gromacs/analysisdata/modules/displacement.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// Legacy include.
+#include "smalloc.h"
+
+#include "gromacs/analysisdata/modules/histogram.h"
+#include "gromacs/basicmath.h"
+#include "gromacs/fatalerror/fatalerror.h"
+
+#include "displacement-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AnalysisDataDisplacementModule::Impl
+ */
+
+AnalysisDataDisplacementModule::Impl::Impl()
+ : nmax(0), tmax(0.0), ndim(3),
+ bFirst(true), t0(0.0), dt(0.0), t(0.0),
+ max_store(-1), nstored(0), oldval(NULL), currd(NULL),
+ histm(NULL)
+{
+}
+
+AnalysisDataDisplacementModule::Impl::~Impl()
+{
+ sfree(oldval);
+ sfree(currd);
+}
+
+/********************************************************************
+ * AnalysisDataDisplacementModule
+ */
+
+AnalysisDataDisplacementModule::AnalysisDataDisplacementModule()
+ : _impl(new Impl())
+{
+ setMultipoint(true);
+}
+
+
+AnalysisDataDisplacementModule::~AnalysisDataDisplacementModule()
+{
+ delete _impl;
+}
+
+
+void
+AnalysisDataDisplacementModule::setMaxTime(real tmax)
+{
+ _impl->tmax = tmax;
+}
+
+
+int
+AnalysisDataDisplacementModule::setMSDHistogram(AnalysisDataBinAverageModule *histm)
+{
+ assert(!_impl->histm);
+ _impl->histm = histm;
+ histm->setIgnoreMissing(true);
+ return addModule(histm);
+}
+
+
+int
+AnalysisDataDisplacementModule::frameCount() const
+{
+ return _impl->nstored > 1 ? _impl->nstored - 1 : 0;
+}
+
+
+int
+AnalysisDataDisplacementModule::getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present) const
+{
+ return eedataDataNotAvailable;
+}
+
+
+int
+AnalysisDataDisplacementModule::requestStorage(int nframes)
+{
+ GMX_ERROR(eeNotImplemented, "Displacement storage not supported");
+}
+
+
+int
+AnalysisDataDisplacementModule::flags() const
+{
+ return efAllowMulticolumn;
+}
+
+
+int
+AnalysisDataDisplacementModule::dataStarted(AbstractAnalysisData *data)
+{
+ if (data->columnCount() % _impl->ndim != 0)
+ {
+ GMX_ERROR(eeInvalidValue, "Data has incorrect number of columns");
+ }
+ _impl->nmax = data->columnCount();
+ snew(_impl->oldval, _impl->nmax);
+ _impl->ci = -_impl->nmax;
+
+ int ncol = _impl->nmax / _impl->ndim + 1;
+ snew(_impl->currd, ncol);
+ setColumnCount(ncol);
+
+ return 0;
+}
+
+
+int
+AnalysisDataDisplacementModule::frameStarted(real x, real dx)
+{
+ // Initialize times.
+ if (_impl->bFirst)
+ {
+ _impl->t0 = x;
+ }
+ else if (_impl->dt <= 0)
+ {
+ _impl->dt = x - _impl->t0;
+ if (_impl->dt < 0 || gmx_within_tol(_impl->dt, 0.0, GMX_REAL_EPS))
+ {
+ GMX_ERROR(eeInvalidInput, "Identical or decreasing frame times");
+ }
+ }
+ else
+ {
+ if (!gmx_within_tol(x - _impl->t, _impl->dt, GMX_REAL_EPS))
+ {
+ GMX_ERROR(eeInvalidInput, "Frames not evenly spaced");
+ }
+ }
+ _impl->t = x;
+
+ // Allocate memory for all the positions once it is possible.
+ if (_impl->max_store == -1 && !_impl->bFirst)
+ {
+ _impl->max_store = _impl->nmax * (int)(_impl->tmax/_impl->dt + 1);
+ srenew(_impl->oldval, _impl->max_store);
+ }
+
+ // Increment the index where current positions are stored.
+ _impl->ci += _impl->nmax;
+ if (_impl->ci >= _impl->max_store)
+ {
+ _impl->ci = 0;
+ }
+
+/*
+ for (int i = 0; i < _impl->nmax; ++i)
+ {
+ _impl->p[_impl->ci + i].bPres = false;
+ }
+*/
+ _impl->nstored++;
+ _impl->bFirst = false;
+ return 0;
+}
+
+
+int
+AnalysisDataDisplacementModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ if (firstcol % _impl->ndim != 0 || n % _impl->ndim != 0)
+ {
+ GMX_ERROR(eeInvalidValue, "Partial data points");
+ }
+ for (int i = firstcol; i < firstcol + n; ++i)
+ {
+ _impl->oldval[_impl->ci + i] = y[i];
+ }
+ return 0;
+}
+
+
+int
+AnalysisDataDisplacementModule::frameFinished()
+{
+ if (_impl->nstored <= 1)
+ {
+ return 0;
+ }
+
+ int step, i;
+ int rc;
+
+ if (_impl->nstored == 2)
+ {
+ if (_impl->histm)
+ {
+ _impl->histm->initNBins(0, _impl->dt,
+ _impl->max_store / _impl->nmax, true);
+ }
+ rc = notifyDataStart();
+ if (rc != 0)
+ {
+ _impl->nstored = 1;
+ return rc;
+ }
+ }
+ rc = notifyFrameStart(_impl->t, 0);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ for (i = _impl->ci - _impl->nmax, step = 1;
+ step < _impl->nstored && i != _impl->ci;
+ i -= _impl->nmax, ++step)
+ {
+ if (i < 0)
+ {
+ i += _impl->max_store;
+ }
+ _impl->currd[0] = step * _impl->dt;
+ int k = 1;
+ for (int j = 0; j < _impl->nmax; j += _impl->ndim, ++k)
+ {
+ real dist2 = 0.0;
+
+ for (int d = 0; d < _impl->ndim; ++d)
+ {
+ dist2 += sqr(_impl->oldval[_impl->ci + j + d]
+ - _impl->oldval[i + j + d]);
+ }
+ _impl->currd[k] = dist2;
+ }
+ rc = notifyPointsAdd(0, k, _impl->currd, NULL, NULL);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+
+ return notifyFrameFinish();
+}
+
+
+int
+AnalysisDataDisplacementModule::dataFinished()
+{
+ if (_impl->nstored >= 2)
+ {
+ return notifyDataFinish();
+ }
+ return 0;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataDisplacementModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_DISPLACEMENT_H
+#define GMX_ANALYSISDATA_MODULES_DISPLACEMENT_H
+
+#include "../analysisdata.h"
+#include "../datamodule.h"
+
+namespace gmx
+{
+
+class AnalysisDataBinAverageModule;
+
+/*! \brief
+ * Data module for calculating displacements.
+ *
+ * Output data contains a frame for each frame in the input data except the
+ * first one. For each frame, there can be multiple points, each of which
+ * describes displacement for a certain time difference ending that that frame.
+ * The first column contains the time difference (backwards from the current
+ * frame), and the remaining columns the sizes of the displacements.
+ *
+ * Current implementation is not very generic, but should be easy to extend.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataDisplacementModule : public AbstractAnalysisData,
+ public AnalysisDataModuleInterface
+{
+ public:
+ AnalysisDataDisplacementModule();
+ virtual ~AnalysisDataDisplacementModule();
+
+ /*! \brief
+ * Sets the largest displacement time to be calculated.
+ */
+ void setMaxTime(real tmax);
+ /*! \brief
+ * Sets an histogram module that will receive a MSD histogram.
+ *
+ * If this function is not called, no histogram is calculated.
+ */
+ int setMSDHistogram(AnalysisDataBinAverageModule *histm);
+
+ virtual int frameCount() const;
+ virtual int getDataWErr(int index, real *x, real *dx,
+ const real **y, const real **dy,
+ const bool **present = 0) const;
+ virtual int requestStorage(int nframes = -1);
+
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+ virtual int frameFinished();
+ virtual int dataFinished();
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in histogram.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "gromacs/analysisdata/modules/histogram.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <cassert>
+#include <cmath>
+
+// Legacy include.
+#include "smalloc.h"
+
+#include "gromacs/basicmath.h"
+#include "gromacs/fatalerror/fatalerror.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AbstractHistogramModule
+ */
+
+AbstractHistogramModule::AbstractHistogramModule()
+ : _hist(NULL), _averager(NULL), _nbins(0)
+{
+}
+
+AbstractHistogramModule::~AbstractHistogramModule()
+{
+ sfree(_hist);
+}
+
+
+int
+AbstractHistogramModule::initNBins(real miny, real binw, int nbins,
+ bool bIntegerBins)
+{
+ assert(nbins > 0 && binw > 0);
+ if (bIntegerBins)
+ {
+ miny -= 0.5*binw;
+ }
+ _nbins = nbins;
+ _miny = miny;
+ _binwidth = binw;
+ _maxy = miny + nbins * binw;
+ _invbw = 1.0/binw;
+ setColumnCount(_nbins);
+ return 0;
+}
+
+
+int
+AbstractHistogramModule::initRange(real miny, real maxy, real binw,
+ bool bIntegerBins)
+{
+ assert(miny < maxy && binw > 0);
+ if (bIntegerBins)
+ {
+ _nbins = ceil((maxy - miny) / binw) + 1;
+ miny -= 0.5 * binw;
+ maxy = miny + _nbins * binw;
+ }
+ else
+ {
+ miny = binw * floor(miny / binw);
+ maxy = binw * ceil(maxy / binw);
+ if (miny != 0)
+ {
+ miny -= binw;
+ }
+ maxy += binw;
+ _nbins = (int)((maxy - miny) / binw + 0.5);
+ }
+ _miny = miny;
+ _maxy = maxy;
+ _binwidth = binw;
+ _invbw = 1.0/binw;
+ setColumnCount(_nbins);
+ return 0;
+}
+
+
+void
+AbstractHistogramModule::setAll(bool bAll)
+{
+ _bAll = bAll;
+}
+
+
+int
+AbstractHistogramModule::findBin(real y) const
+{
+ if (y < _miny)
+ {
+ return _bAll ? 0 : -1;
+ }
+ else if (y >= _maxy)
+ {
+ return _bAll ? _nbins-1 : -1;
+ }
+ return (int)((y - _miny) * _invbw);
+}
+
+
+HistogramAverageModule *
+AbstractHistogramModule::averager()
+{
+ if (!_averager)
+ {
+ createAverager();
+ }
+ return _averager;
+}
+
+
+int
+AbstractHistogramModule::flags() const
+{
+ return efAllowMultipoint;
+}
+
+
+int
+AbstractHistogramModule::dataStarted(AbstractAnalysisData *data)
+{
+ if (!_averager)
+ {
+ createAverager();
+ }
+ _averager->setXAxis(_miny + 0.5 * _binwidth, _binwidth);
+ snew(_hist, nbins());
+ return startDataStore();
+}
+
+
+int
+AbstractHistogramModule::frameStarted(real x, real dx)
+{
+ for (int i = 0; i < nbins(); ++i)
+ {
+ _hist[i] = 0.0;
+ }
+ return startNextFrame(x, dx);
+}
+
+
+int
+AbstractHistogramModule::frameFinished()
+{
+ return storeThisFrame(_hist, NULL, NULL);
+}
+
+
+int
+AbstractHistogramModule::dataFinished()
+{
+ return notifyDataFinish();
+}
+
+
+void
+AbstractHistogramModule::createAverager()
+{
+ _averager = new HistogramAverageModule();
+ addModule(_averager);
+ _averager->setXAxis(_miny + 0.5 * _binwidth, _binwidth);
+}
+
+
+/********************************************************************
+ * HistogramAverageModule
+ */
+
+HistogramAverageModule::HistogramAverageModule()
+ : _nframes(0), _bIgnoreMissing(false)
+{
+ setColumnCount(2);
+}
+
+
+void
+HistogramAverageModule::setIgnoreMissing(bool bIgnoreMissing)
+{
+ _bIgnoreMissing = bIgnoreMissing;
+ setColumnCount(bIgnoreMissing ? 3 : 2);
+}
+
+
+int
+HistogramAverageModule::flags() const
+{
+ return efAllowMulticolumn | efAllowMissing;
+}
+
+
+int
+HistogramAverageModule::dataStarted(AbstractAnalysisData *data)
+{
+ setRowCount(data->columnCount());
+ return 0;
+}
+
+
+int
+HistogramAverageModule::frameStarted(real x, real dx)
+{
+ return 0;
+}
+
+
+int
+HistogramAverageModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ value(firstcol + i, 0) += y[i];
+ value(firstcol + i, 1) += y[i] * y[i];
+ }
+ if (_bIgnoreMissing)
+ {
+ assert(present != NULL);
+ for (int i = 0; i < n; ++i)
+ {
+ if (present[i])
+ {
+ value(firstcol + i, 2) += 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+int
+HistogramAverageModule::frameFinished()
+{
+ ++_nframes;
+ return 0;
+}
+
+
+int
+HistogramAverageModule::dataFinished()
+{
+ for (int i = 0; i < rowCount(); ++i)
+ {
+ real ave = 0.0;
+ real std = 0.0;
+ if (_bIgnoreMissing)
+ {
+ if (value(i, 2) > 0)
+ {
+ ave = value(i, 0) / value(i, 2);
+ std = sqrt(value(i, 1) / value(i, 2) - ave * ave);
+ }
+ }
+ else
+ {
+ ave = value(i, 0) / _nframes;
+ std = sqrt(value(i, 1) / _nframes - ave * ave);
+ }
+ setValue(i, 0, ave);
+ setValue(i, 1, std);
+ }
+ return 0;
+}
+
+
+HistogramAverageModule *
+HistogramAverageModule::resampleDoubleBinWidth(bool bIntegerBins) const
+{
+ HistogramAverageModule *dest = new HistogramAverageModule();
+ int nbins = rowCount() / 2;
+ dest->setRowCount(nbins);
+ real minx = xstart() + xstep() / 2;
+ if (bIntegerBins)
+ {
+ minx -= xstep();
+ }
+ dest->setXAxis(minx, xstep() * 2);
+
+ int i, j;
+ for (i = j = 0; i < nbins; ++i)
+ {
+ real v, ve;
+ if (bIntegerBins && i == 0)
+ {
+ v = value(0, 0);
+ ve = sqr(value(0, 1));
+ ++j;
+ }
+ else
+ {
+ v = value(j, 0) + value(j+1, 0);
+ ve = sqr(value(j, 1)) + sqr(value(j+1, 1));
+ j += 2;
+ }
+ ve = sqrt(ve);
+ dest->setValue(i, 0, v);
+ dest->setValue(i, 1, ve);
+ }
+ return dest;
+}
+
+
+HistogramAverageModule *
+HistogramAverageModule::clone() const
+{
+ HistogramAverageModule *dest = new HistogramAverageModule();
+ copyContents(this, dest);
+ return dest;
+}
+
+
+void
+HistogramAverageModule::normalizeProbability()
+{
+ real sum = 0;
+ for (int i = 0; i < rowCount(); ++i)
+ {
+ sum += value(i, 0);
+ }
+ scale(1.0 / (sum * xstep()));
+}
+
+
+void
+HistogramAverageModule::scale(real norm)
+{
+ for (int i = 0; i < rowCount(); ++i)
+ {
+ value(i, 0) *= norm;
+ value(i, 1) *= norm;
+ }
+}
+
+
+void
+HistogramAverageModule::scaleVector(real norm[])
+{
+ for (int i = 0; i < rowCount(); ++i)
+ {
+ value(i, 0) *= norm[i];
+ value(i, 1) *= norm[i];
+ }
+}
+
+
+/********************************************************************
+ * AnalysisDataSimpleHistogramModule
+ */
+
+int
+AnalysisDataSimpleHistogramModule::pointsAdded(real /*x*/, real /*dx*/,
+ int /*firstcol*/, int n,
+ const real *y, const real * /*dy*/,
+ const bool * /*present*/)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ int bin = findBin(y[i]);
+ _hist[bin] += 1;
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * AnalysisDataWeightedHistogramModule
+ */
+
+int
+AnalysisDataWeightedHistogramModule::flags() const
+{
+ return AbstractHistogramModule::flags() | efAllowMulticolumn;
+}
+
+
+int
+AnalysisDataWeightedHistogramModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ if (firstcol != 0 || n < 2)
+ {
+ GMX_ERROR(eeInvalidValue, "Invalid data layout");
+ }
+ int bin = findBin(y[0]);
+ for (int i = 1; i < n; ++i)
+ {
+ _hist[bin] += y[i];
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * AnalysisDataBinAverageModule
+ */
+
+AnalysisDataBinAverageModule::AnalysisDataBinAverageModule()
+ : _n(NULL), _present(NULL), _bIgnoreMissing(false)
+{
+}
+
+AnalysisDataBinAverageModule::~AnalysisDataBinAverageModule()
+{
+ sfree(_n);
+ sfree(_present);
+}
+
+
+void
+AnalysisDataBinAverageModule::setIgnoreMissing(bool bIgnoreMissing)
+{
+ // Changes can only be made before there is data.
+ assert(!_n);
+ _bIgnoreMissing = bIgnoreMissing;
+ averager()->setIgnoreMissing(bIgnoreMissing);
+}
+
+
+int
+AnalysisDataBinAverageModule::flags() const
+{
+ return AbstractHistogramModule::flags() | efAllowMulticolumn;
+}
+
+
+int
+AnalysisDataBinAverageModule::dataStarted(AbstractAnalysisData *data)
+{
+ snew(_n, nbins());
+ snew(_present, nbins());
+ return AbstractHistogramModule::dataStarted(data);
+}
+
+
+int
+AnalysisDataBinAverageModule::frameStarted(real x, real dx)
+{
+ for (int i = 0; i < nbins(); ++i)
+ {
+ _n[i] = 0.0;
+ }
+ return AbstractHistogramModule::frameStarted(x, dx);
+}
+
+
+int
+AnalysisDataBinAverageModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ if (firstcol != 0 || n < 2)
+ {
+ GMX_ERROR(eeInvalidValue, "Invalid data layout");
+ }
+ int bin = findBin(y[0]);
+ for (int i = 1; i < n; ++i)
+ {
+ _hist[bin] += y[i];
+ }
+ _n[bin] += n - 1;
+ return 0;
+}
+
+
+int
+AnalysisDataBinAverageModule::frameFinished()
+{
+ for (int i = 0; i < nbins(); ++i)
+ {
+ _present[i] = (_n[i] > 0);
+ if (_n[i] > 0)
+ {
+ _hist[i] /= _n[i];
+ }
+ }
+ if (!_bIgnoreMissing)
+ {
+ return AbstractHistogramModule::frameFinished();
+ }
+ return storeThisFrame(_hist, NULL, _present);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares analysis data modules for calculating histograms.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_HISTOGRAM_H
+#define GMX_ANALYSISDATA_MODULES_HISTOGRAM_H
+
+#include "../analysisdata.h"
+#include "../arraydata.h"
+#include "../datamodule.h"
+
+namespace gmx
+{
+
+class HistogramAverageModule;
+
+/*! \brief
+ * Abstract base class for per-frame histogram modules.
+ *
+ * \ingroup module_analysisdata
+ */
+class AbstractHistogramModule : public AbstractAnalysisDataStored,
+ public AnalysisDataModuleInterface
+{
+ public:
+ virtual ~AbstractHistogramModule();
+
+ /*! \brief
+ * Initializes the histogram using bin width and the number of bins.
+ */
+ int initNBins(real miny, real binw, int nbins,
+ bool bIntegerBins = false);
+ /*! \brief
+ * Initializes the histogram using a range and a bin width.
+ */
+ int initRange(real miny, real maxy, real binw,
+ bool bIntegerBins = false);
+ /*! \brief
+ * Sets the histogram to match all values.
+ *
+ * If \p bAll is true, the histogram behaves as if the bins at the ends
+ * extended to +-infinity.
+ */
+ void setAll(bool bAll);
+
+ /*! \brief
+ * Returns the average histogram over all frames.
+ *
+ * Can be called already before the histogram is calculated to
+ * customize the way the average histogram is calculated.
+ *
+ * \see HistogramAverageModule
+ */
+ HistogramAverageModule *averager();
+
+ //! Returns the number of bins in the histogram.
+ int nbins() const { return _nbins; }
+ //! Returns the width of a bin in the histogram.
+ real binwidth() const { return _binwidth; }
+ //! Returns a zero-based bin index for a value, or -1 if not in range.
+ int findBin(real y) const;
+
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present) = 0;
+ virtual int frameFinished();
+ virtual int dataFinished();
+
+ protected:
+ AbstractHistogramModule();
+
+ //! Actual histogram data.
+ real *_hist;
+
+ private:
+ void createAverager();
+
+ HistogramAverageModule *_averager;
+ int _nbins;
+ real _miny;
+ real _maxy;
+ real _binwidth;
+ real _invbw;
+ bool _bAll;
+
+ // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for averaging histograms over frames.
+ *
+ * The averaging module for a per-frame histogram is always created by the
+ * AbstractHistogramModule class, and can be accessed using
+ * AbstractHistogramModule::averager().
+ * The user can alter some properties of the average histogram directly, but
+ * the main use of the object is to postprocess the histogram once the
+ * calculation is finished.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class HistogramAverageModule : public AbstractAnalysisArrayData,
+ public AnalysisDataModuleInterface
+{
+ public:
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+ virtual int frameFinished();
+ virtual int dataFinished();
+
+ /*! \brief
+ * Sets the averager to ignore missing values.
+ */
+ void setIgnoreMissing(bool bIgnoreMissing);
+
+ /*! \brief
+ * Creates a copy of the histogram with double the bin width.
+ */
+ HistogramAverageModule *resampleDoubleBinWidth(bool bIntegerBins) const;
+ //! Creates a deep copy of the histogram.
+ HistogramAverageModule *clone() const;
+ //! Normalizes the histogram such that the integral over it is one.
+ void normalizeProbability();
+ //! Scales the value of each bin by an uniform scaling factor.
+ void scale(real norm);
+ //! Scales the value of each bin by a different scaling factor.
+ void scaleVector(real norm[]);
+ /*! \brief
+ * Notifies attached modules of the histogram data.
+ *
+ * After this function has been called, it is no longer possible to
+ * alter the histogram.
+ */
+ int done() { return AbstractAnalysisArrayData::valuesReady(); }
+
+ private:
+ HistogramAverageModule();
+
+ int _nframes;
+ bool _bIgnoreMissing;
+
+ friend class AbstractHistogramModule;
+
+ // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for per-frame histograms.
+ *
+ * Output data contains the same number of frames as the input data.
+ * Each frame contains the histogram for the points in that frame.
+ * All input columns are averaged into the same histogram.
+ * The number of columns equals the number of bins in the histogram.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataSimpleHistogramModule : public AbstractHistogramModule
+{
+ public:
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+
+ // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for per-frame weighted histograms.
+ *
+ * Output data contains the same number of frames as the input data.
+ * Each frame contains the histogram for the points in that frame, interpreted
+ * such that the first column passed to pointsAdded() determines the bin and
+ * the rest give weights to be added to that bin (input data should have at
+ * least two colums, and at least two columns should be added at the same time).
+ * All input columns are averaged into the same histogram.
+ * The number of columns equals the number of bins in the histogram.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataWeightedHistogramModule : public AbstractHistogramModule
+{
+ public:
+ virtual int flags() const;
+
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+
+ // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for per-frame bin averages.
+ *
+ * Output data contains the same number of frames as the input data.
+ * Each frame contains the average for the points in that frame within each bin.
+ * The input data is interpreted such that the first column passed to
+ * pointsAdded() determines the bin and the rest give values to be added to
+ * that bin (input data should have at least two colums, and at least two
+ * columns should be added at the same time).
+ * All input columns are averaged into the same histogram.
+ * The number of columns equals the number of bins.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataBinAverageModule : public AbstractHistogramModule
+{
+ public:
+ AnalysisDataBinAverageModule();
+ virtual ~AnalysisDataBinAverageModule();
+
+ //! Ignore missing bins in the average histogram.
+ void setIgnoreMissing(bool bIgnoreMissing);
+
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+ virtual int frameFinished();
+
+ private:
+ int *_n;
+ bool *_present;
+ bool _bIgnoreMissing;
+
+ // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::AbstractPlotModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_PLOT_IMPL_H
+#define GMX_ANALYSISDATA_MODULES_PLOT_IMPL_H
+
+#include <string>
+#include <vector>
+
+#include "plot.h"
+
+namespace gmx
+{
+
+class Options;
+
+class AbstractPlotModule::Impl
+{
+ public:
+ explicit Impl(const Options &options);
+ ~Impl();
+
+ void closeFile();
+
+ std::string fnm;
+ FILE *fp;
+
+ bool bPlain;
+ output_env_t oenv;
+ SelectionCollection *sel;
+ std::string title;
+ std::string subtitle;
+ std::string xlabel;
+ std::string ylabel;
+ std::vector<std::string> leg;
+ char xfmt[15];
+ char yfmt[15];
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in plot.h.
+ *
+ * \ingroup module_analysisdata
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include "gromacs/analysisdata/modules/plot.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+
+#include <gmxfio.h>
+#include <statutil.h>
+#include <vec.h>
+#include <xvgr.h>
+
+#include "gromacs/options/globalproperties.h"
+#include "gromacs/options/options.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/selection/selectioncollection.h"
+
+#include "plot-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AbstractPlotModule::Impl
+ */
+
+AbstractPlotModule::Impl::Impl(const Options &options)
+ : fp(NULL), bPlain(false),
+ oenv(options.globalProperties().output_env()),
+ sel(options.globalProperties().selectionCollection())
+{
+ strcpy(xfmt, "%11.3f");
+ strcpy(yfmt, " %8.3f");
+}
+
+AbstractPlotModule::Impl::~Impl()
+{
+ closeFile();
+}
+
+
+void
+AbstractPlotModule::Impl::closeFile()
+{
+ if (fp)
+ {
+ if (bPlain)
+ {
+ gmx_fio_fclose(fp);
+ }
+ else
+ {
+ xvgrclose(fp);
+ }
+ fp = NULL;
+ }
+}
+
+
+/********************************************************************
+ * AbstractPlotModule
+ */
+
+AbstractPlotModule::AbstractPlotModule(const Options &options)
+ : _impl(new Impl(options))
+{
+}
+
+
+AbstractPlotModule::~AbstractPlotModule()
+{
+ delete _impl;
+}
+
+
+void
+AbstractPlotModule::setFileName(const std::string &fnm)
+{
+ _impl->fnm = fnm;
+}
+
+
+void
+AbstractPlotModule::setPlainOutput(bool bPlain)
+{
+ _impl->bPlain = bPlain;
+}
+
+
+void
+AbstractPlotModule::setTitle(const char *title)
+{
+ _impl->title = title;
+}
+
+
+void
+AbstractPlotModule::setSubtitle(const char *subtitle)
+{
+ _impl->subtitle = subtitle;
+}
+
+
+void
+AbstractPlotModule::setXLabel(const char *label)
+{
+ _impl->xlabel = label;
+}
+
+
+void
+AbstractPlotModule::setXTimeLabel()
+{
+ _impl->xlabel = output_env_get_xvgr_tlabel(_impl->oenv);
+}
+
+
+void
+AbstractPlotModule::setYLabel(const char *label)
+{
+ _impl->ylabel = label;
+}
+
+
+void
+AbstractPlotModule::setLegend(int nsets, const char * const *setname)
+{
+ _impl->leg.reserve(_impl->leg.size() + nsets);
+ for (int i = 0; i < nsets; ++i)
+ {
+ appendLegend(setname[i]);
+ }
+}
+
+
+void
+AbstractPlotModule::appendLegend(const char *setname)
+{
+ _impl->leg.push_back(setname);
+}
+
+
+void
+AbstractPlotModule::setXFormat(int width, int prec, char fmt)
+{
+ assert(width >= 0 && prec >= 0 && width <= 99 && prec <= 99);
+ assert(strchr("eEfFgG", fmt) != NULL);
+ sprintf(_impl->xfmt, "%%%d.%d%c", width, prec, fmt);
+}
+
+
+void
+AbstractPlotModule::setYFormat(int width, int prec, char fmt)
+{
+ assert(width >= 0 && prec >= 0 && width <= 99 && prec <= 99);
+ assert(strchr("eEfFgG", fmt) != NULL);
+ sprintf(_impl->yfmt, " %%%d.%d%c", width, prec, fmt);
+}
+
+
+int
+AbstractPlotModule::flags() const
+{
+ return efAllowMulticolumn | efAllowMultipoint;
+}
+
+
+int
+AbstractPlotModule::dataStarted(AbstractAnalysisData *data)
+{
+ if (!_impl->fnm.empty())
+ {
+ if (_impl->bPlain)
+ {
+ _impl->fp = gmx_fio_fopen(_impl->fnm.c_str(), "w");
+ }
+ else
+ {
+ _impl->fp = xvgropen(_impl->fnm.c_str(), _impl->title.c_str(),
+ _impl->xlabel.c_str(), _impl->ylabel.c_str(),
+ _impl->oenv);
+ if (_impl->sel != NULL)
+ {
+ _impl->sel->printXvgrInfo(_impl->fp, _impl->oenv);
+ }
+ if (!_impl->subtitle.empty())
+ {
+ xvgr_subtitle(_impl->fp, _impl->subtitle.c_str(), _impl->oenv);
+ }
+ if (output_env_get_print_xvgr_codes(_impl->oenv)
+ && !_impl->leg.empty())
+ {
+ const char **leg;
+
+ leg = new const char *[_impl->leg.size()];
+ for (size_t i = 0; i < _impl->leg.size(); ++i)
+ {
+ leg[i] = _impl->leg[i].c_str();
+ }
+ xvgr_legend(_impl->fp, _impl->leg.size(), leg, _impl->oenv);
+ delete [] leg;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int
+AbstractPlotModule::frameStarted(real x, real dx)
+{
+ if (!isFileOpen())
+ {
+ return 0;
+ }
+ std::fprintf(_impl->fp, _impl->xfmt, x);
+ return 0;
+}
+
+
+int
+AbstractPlotModule::frameFinished()
+{
+ if (!isFileOpen())
+ {
+ return 0;
+ }
+ std::fprintf(_impl->fp, "\n");
+ return 0;
+}
+
+
+int
+AbstractPlotModule::dataFinished()
+{
+ _impl->closeFile();
+ return 0;
+}
+
+
+bool
+AbstractPlotModule::isFileOpen() const
+{
+ return _impl->fp != NULL;
+}
+
+
+void
+AbstractPlotModule::writeValue(real value) const
+{
+ assert(isFileOpen());
+ std::fprintf(_impl->fp, _impl->yfmt, value);
+}
+
+
+/********************************************************************
+ * DataPlotModule
+ */
+
+AnalysisDataPlotModule::AnalysisDataPlotModule(const Options &options)
+ : AbstractPlotModule(options)
+{
+}
+
+
+int
+AnalysisDataPlotModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ if (!isFileOpen())
+ {
+ return 0;
+ }
+ for (int i = 0; i < n; ++i)
+ {
+ writeValue(y[i]);
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * DataVectorPlotModule
+ */
+
+AnalysisDataVectorPlotModule::AnalysisDataVectorPlotModule(const Options &options)
+ : AbstractPlotModule(options)
+{
+ for (int i = 0; i < DIM; ++i)
+ {
+ _bWrite[i] = true;
+ }
+ _bWrite[DIM] = false;
+}
+
+
+void
+AnalysisDataVectorPlotModule::setWriteX(bool bWrite)
+{
+ _bWrite[XX] = bWrite;
+}
+
+
+void
+AnalysisDataVectorPlotModule::setWriteY(bool bWrite)
+{
+ _bWrite[YY] = bWrite;
+}
+
+
+void
+AnalysisDataVectorPlotModule::setWriteZ(bool bWrite)
+{
+ _bWrite[ZZ] = bWrite;
+}
+
+
+void
+AnalysisDataVectorPlotModule::setWriteNorm(bool bWrite)
+{
+ _bWrite[DIM] = bWrite;
+}
+
+
+void
+AnalysisDataVectorPlotModule::setWriteMask(bool bWrite[4])
+{
+ for (int i = 0; i < DIM + 1; ++i)
+ {
+ _bWrite[i] = bWrite[i];
+ }
+}
+
+
+int
+AnalysisDataVectorPlotModule::pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present)
+{
+ if (firstcol % DIM != 0)
+ {
+ GMX_ERROR(eeInvalidValue, "Partial data points");
+ }
+ if (!isFileOpen())
+ {
+ return 0;
+ }
+ for (int i = 0; i < n; i += 3)
+ {
+ for (int d = 0; d < DIM; ++d)
+ {
+ if (_bWrite[i])
+ {
+ writeValue(y[i + d]);
+ }
+ }
+ if (_bWrite[DIM])
+ {
+ writeValue(norm(&y[i]));
+ }
+ }
+ return 0;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataPlotModule for plotting data (into a file).
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_PLOT_H
+#define GMX_ANALYSISDATA_MODULES_PLOT_H
+
+#include <string>
+
+#include "../datamodule.h"
+
+namespace gmx
+{
+
+class Options;
+
+/*! \brief
+ * Abstract data module for writing data into a file.
+ *
+ * Implements features common to all plotting modules. Subclasses implement
+ * features specific to certain applications (AnalysisDataPlotModule implements
+ * straightforward plotting).
+ *
+ * By default, the data is written into an xvgr file, according to the
+ * options read from the Options object given to the constructor.
+ * For non-xvgr data, it's possible to skip all headers by calling
+ * setPlainOutput().
+ *
+ * Multipoint data is supported, in which case all the points are written to
+ * the output, in the order in which they are added to the data. A single
+ * output line corresponds to a single frame. In most cases with multipoint
+ * data, setPlainOutput() should be called since the output does not make sense
+ * as an xvgr file, but this is not enforced.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AbstractPlotModule : public AnalysisDataModuleInterface
+{
+ public:
+ virtual ~AbstractPlotModule();
+
+ /*! \brief
+ * Set the output file name.
+ *
+ * If no file name is set (or if \p fnm is NULL), no output occurs.
+ */
+ void setFileName(const std::string &fnm);
+ /*! \brief
+ * Set plain output.
+ *
+ * If \p bPlain is true, no xvgr headers are written to the file.
+ * In this case, only setXFormat() and setYFormat() methods have any
+ * effect on the output.
+ */
+ void setPlainOutput(bool bPlain);
+ /*! \brief
+ * Set plot title.
+ */
+ void setTitle(const char *title);
+ /*! \brief
+ * Set plot subtitle.
+ */
+ void setSubtitle(const char *subtitle);
+ /*! \brief
+ * Set X axis label.
+ */
+ void setXLabel(const char *label);
+ /*! \brief
+ * Set X axis label for time.
+ */
+ void setXTimeLabel();
+ /*! \brief
+ * Set Y axis label.
+ */
+ void setYLabel(const char *label);
+ /*! \brief
+ * Add legend from an array of strings.
+ *
+ * Multiple calls to setLegend() and/or appendLegend() are added
+ * together.
+ */
+ void setLegend(int nsets, const char * const *setname);
+ /*! \brief
+ * Add a legend string for the next data set.
+ *
+ * Multiple calls to setLegend() and/or appendLegend() are added
+ * together.
+ */
+ void appendLegend(const char *setname);
+ /*! \brief
+ * Set field width and precision for X value output.
+ */
+ void setXFormat(int width, int prec, char fmt = 'f');
+ /*! \brief
+ * Set field width and precision for Y value output.
+ */
+ void setYFormat(int width, int prec, char fmt = 'f');
+
+ virtual int flags() const;
+
+ virtual int dataStarted(AbstractAnalysisData *data);
+ virtual int frameStarted(real x, real dx);
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present) = 0;
+ virtual int frameFinished();
+ virtual int dataFinished();
+
+ protected:
+ explicit AbstractPlotModule(const Options &options);
+
+ bool isFileOpen() const;
+ void writeValue(real value) const;
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ AbstractPlotModule(const AbstractPlotModule &);
+ void operator =(const AbstractPlotModule &);
+};
+
+
+/*! \brief
+ * Plotting module for straightforward plotting of data.
+ *
+ * See AbstractPlotModule for common plotting options.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataPlotModule : public AbstractPlotModule
+{
+ public:
+ explicit AnalysisDataPlotModule(const Options &options);
+
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+
+ // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Plotting module specifically for data consisting of vectors.
+ *
+ * See AbstractPlotModule for common plotting options.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataVectorPlotModule : public AbstractPlotModule
+{
+ public:
+ explicit AnalysisDataVectorPlotModule(const Options &options);
+
+ void setWriteX(bool bWrite);
+ void setWriteY(bool bWrite);
+ void setWriteZ(bool bWrite);
+ void setWriteNorm(bool bWrite);
+ void setWriteMask(bool bWrite[4]);
+
+ virtual int pointsAdded(real x, real dx, int firstcol, int n,
+ const real *y, const real *dy,
+ const bool *present);
+
+ private:
+ bool _bWrite[4];
+
+ // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+#ifndef GMX_BASICMATH_H
+#define GMX_BASICMATH_H
+
+#include <cmath>
+
+#include "types/simple.h"
+
+namespace gmx
+{
+
+template <typename T> static inline
+T sqr(const T &value)
+{
+ return value * value;
+}
+
+/*! \brief
+ * Check if two numbers are within a tolerance
+ *
+ * This routine checks if the relative difference between two numbers is
+ * approximately within the given tolerance, defined as
+ * fabs(f1-f2)<=tolerance*fabs(f1+f2).
+ *
+ * To check if two floating-point numbers are almost identical, use this routine
+ * with the tolerance GMX_REAL_EPS, or GMX_DOUBLE_EPS if the check should be
+ * done in double regardless of Gromacs precision.
+ *
+ * To check if two algorithms produce similar results you will normally need
+ * to relax the tolerance significantly since many operations (e.g. summation)
+ * accumulate floating point errors.
+ *
+ * \param f1 First number to compare
+ * \param f2 Second number to compare
+ * \param tol Tolerance to use
+ *
+ * \return 1 if the relative difference is within tolerance, 0 if not.
+ */
+static int
+gmx_within_tol(double f1,
+ double f2,
+ double tol)
+{
+ /* The or-equal is important - otherwise we return false if f1==f2==0 */
+ if (std::fabs(f1-f2) <= tol * 0.5 * (std::fabs(f1) + std::fabs(f2)))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+
+/*! \brief
+ * Check if a number is smaller than some preset safe minimum
+ * value, currently defined as GMX_REAL_MIN/GMX_REAL_EPS.
+ *
+ * If a number is smaller than this value we risk numerical overflow
+ * if any number larger than 1.0/GMX_REAL_EPS is divided by it.
+ *
+ * \return 1 if 'almost' numerically zero, 0 otherwise.
+ */
+static int
+gmx_numzero(double a)
+{
+ return gmx_within_tol(a,0.0,GMX_REAL_MIN/GMX_REAL_EPS);
+}
+
+}
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \defgroup module_errorreporting Reporting of Non-Fatal Errors
+ * \ingroup group_utilitymodules
+ * \brief
+ * Provides functions and classes for reporting non-fatal errors.
+ *
+ * Facilities for customizable reporting of non-fatal errors are provided by an
+ * abstract class AbstractErrorReporter. Objects of this class can be passed
+ * to functions for reporting non-fatal errors. The caller can then create a
+ * reporter object derived from AbstractErrorReporter to implement desired
+ * behavior for reporting the errors to the user. Two basic reporter classes
+ * are provided by this module: EmptyErrorReporter that only counts the number
+ * of errors occurred, and StandardErrorReporter that writes the errors to
+ * standard error.
+ *
+ * An ErrorContext class is also provided to ease implementation of functions
+ * that need to report errors that originate deeper in the call stack.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+/*! \file
+ * \brief
+ * Public API convenience header for non-fatal error reporting.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+#ifndef GMX_ERRORREPORTING_H
+#define GMX_ERRORREPORTING_H
+
+#include "errorreporting/abstracterrorreporter.h"
+#include "errorreporting/emptyerrorreporter.h"
+#include "errorreporting/errorcontext.h"
+#include "errorreporting/standarderrorreporter.h"
+
+#endif
--- /dev/null
+file(GLOB ERRORREPORTING_SOURCES *.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${ERRORREPORTING_SOURCES} PARENT_SCOPE)
+
+set(ERRORREPORTING_PUBLIC_HEADERS
+ abstracterrorreporter.h
+ emptyerrorreporter.h
+ errorcontext.h
+ standarderrorreporter.h)
+install(FILES ${ERRORREPORTING_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/errorreporting
+ COMPONENT development)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Defines ::gmx::AbstractErrorReporter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+#ifndef GMX_ERRORREPORTING_ABSTRACTERRORREPORTER_H
+#define GMX_ERRORREPORTING_ABSTRACTERRORREPORTER_H
+
+#include <string>
+
+namespace gmx
+{
+
+/*! \brief
+ * Abstract base class for error reporters.
+ *
+ * This class provides an interface for reporting non-fatal errors from a
+ * complex function. Such a function should take a pointer to an
+ * AbstractErrorReporter object, and use the provided methods to report any errors
+ * it encounters. If the function calls other functions that can also detect
+ * errors, it can pass the reporter object to these functions as well, possibly
+ * after adding context information using startContext()/finishContext() or the
+ * ErrorContext class. The caller of such a function can then create an error
+ * reporter of their choice and pass it to the function to alter how errors are
+ * reported to the user.
+ *
+ * This abstract implementation provides basic facilities to implement counting
+ * of errors of different type. Derived classes should call the
+ * incrementCount() method from their implementation of the add() method for
+ * this to work properly.
+ *
+ * \see ErrorContext
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+class AbstractErrorReporter
+{
+ public:
+ /*! \brief
+ * Type of error.
+ */
+ enum ErrorType
+ {
+ etNote,
+ etWarning,
+ etError,
+ etNR
+ };
+
+ virtual ~AbstractErrorReporter() {}
+
+ /*! \brief
+ * Starts a context for errors.
+ *
+ * \param[in] name Short description of the context.
+ *
+ * Derived classes should duplicate the string if they need to store
+ * it.
+ *
+ * \see ErrorContext
+ */
+ virtual void startContext(const char *name) = 0;
+ /*! \brief
+ * Reports a new error.
+ *
+ * \param[in] type Type of the error.
+ * \param[in] reason Short description of the error.
+ *
+ * Derived classes should duplicate the string if they need to store
+ * it.
+ */
+ virtual void add(ErrorType type, const char *reason) = 0;
+ /*! \brief
+ * Ends a context started with startContext().
+ *
+ * \see ErrorContext
+ */
+ virtual void finishContext() = 0;
+
+ /*! \brief
+ * Convenience wrapper for startContext(const char *).
+ */
+ void startContext(const std::string &name) { startContext(name.c_str()); }
+ /*! \brief
+ * Convenience wrapper for add(ErrorType, const char *).
+ */
+ void add(ErrorType type, const std::string &reason) { add(type, reason.c_str()); }
+ /*! \brief
+ * Convenience wrapper for add() for adding notes.
+ */
+ void note(const char *reason) { add(etNote, reason); }
+ //! \copydoc note(const char *)
+ void note(const std::string &reason) { add(etNote, reason); }
+ /*! \brief
+ * Convenience wrapper for add() for adding warnings.
+ */
+ void warning(const char *reason) { add(etWarning, reason); }
+ //! \copydoc warning(const char *)
+ void warning(const std::string &reason) { add(etWarning, reason); }
+ /*! \brief
+ * Convenience wrapper for add() for adding errors.
+ */
+ void error(const char *reason) { add(etError, reason); }
+ //! \copydoc error(const char *)
+ void error(const std::string &reason) { add(etError, reason); }
+
+ /*! \brief
+ * Returns the number of errors of particular type that have occurred.
+ */
+ int errorCount(ErrorType type) const { return _count[type]; }
+
+ protected:
+ /*! \brief
+ * Initializes a reporter with zero error counts.
+ */
+ AbstractErrorReporter()
+ {
+ for (int i = 0; i < etNR; ++i)
+ {
+ _count[i] = 0;
+ }
+ }
+
+ /*! \brief
+ * Increment the count of a particular error type.
+ *
+ * Derived classes should call this function from their implementation
+ * of the add() method.
+ */
+ void incrementCount(ErrorType type) { _count[type]++; }
+
+ private:
+ //! Number of errors occurred for each type.
+ int _count[etNR];
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares ::gmx::EmptyErrorReporter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+#ifndef GMX_ERRORREPORTING_EMPTYERRORREPORTER_H
+#define GMX_ERRORREPORTING_EMPTYERRORREPORTER_H
+
+#include "abstracterrorreporter.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Error reporter that only counts the number of different types of errors.
+ *
+ * All descriptions of the errors are discarded.
+ * Mostly useful for testing purposes.
+ *
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+class EmptyErrorReporter : public AbstractErrorReporter
+{
+ public:
+ EmptyErrorReporter() {}
+ virtual ~EmptyErrorReporter() {}
+
+ virtual void startContext(const char *name);
+ virtual void add(ErrorType type, const char *reason);
+ virtual void finishContext();
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Defines ::gmx::ErrorContext.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+#ifndef GMX_ERRORREPORTING_ERRORCONTEXT_H
+#define GMX_ERRORREPORTING_ERRORCONTEXT_H
+
+#include <string>
+
+#include "abstracterrorreporter.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Convenience class for creating an error context.
+ *
+ * This class provides a RAII-style interface to the
+ * AbstractErrorReporter::startContext() and
+ * AbstractErrorReporter::finishContext() methods: finishContext() is called
+ * upon destruction of the object. This avoids the need to call
+ * AbstractErrorReporter::finishContext() on every possible exit point.
+ *
+ * Example usage:
+ * \code
+int function(::gmx::AbstractErrorReporter *errors)
+{
+ ::gmx::ErrorContext errcontext(errors, "In function()");
+ int rc;
+ rc = function2(errors);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = function3(errors);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ // <more processing>
+ return 0;
+}
+ * \endcode
+ *
+ * \see AbstractErrorReporter
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+class ErrorContext
+{
+ public:
+ /*! \brief
+ * Adds a context for the given reporter.
+ */
+ ErrorContext(AbstractErrorReporter *reporter, const char *name)
+ : _reporter(*reporter)
+ {
+ _reporter.startContext(name);
+ }
+ /*! \brief
+ * Adds a context for the given reporter.
+ */
+ ErrorContext(AbstractErrorReporter *reporter, const std::string &name)
+ : _reporter(*reporter)
+ {
+ _reporter.startContext(name);
+ }
+ /*! \brief
+ * Calls AbstractReporter::finishContext() on the wrapped reporter.
+ */
+ ~ErrorContext()
+ {
+ _reporter.finishContext();
+ }
+
+ private:
+ //! The wrapped reporter object.
+ AbstractErrorReporter &_reporter;
+
+ // Disallow copy and assign.
+ ErrorContext(const ErrorContext &);
+ void operator =(const ErrorContext &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements error reporter classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_errorreporting
+ */
+#include <cstdio>
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/errorreporting/emptyerrorreporter.h"
+#include "gromacs/errorreporting/standarderrorreporter.h"
+
+#include "standarderrorreporter-impl.h"
+
+static const char *const error_type_names[] = { "note", "warning", "error" };
+
+namespace gmx
+{
+
+/********************************************************************
+ * EmptyErrorReporter
+ */
+
+void EmptyErrorReporter::startContext(const char * /*name*/)
+{
+}
+
+void EmptyErrorReporter::add(ErrorType type, const char * /*reason*/)
+{
+ incrementCount(type);
+}
+
+void EmptyErrorReporter::finishContext()
+{
+}
+
+/********************************************************************
+ * StandardErrorReporter::Impl
+ */
+
+StandardErrorReporter::Impl::Impl()
+ : _prevContext(-1)
+{
+}
+
+/********************************************************************
+ * StandardErrorReporter
+ */
+
+StandardErrorReporter::StandardErrorReporter()
+ : _impl(new Impl)
+{
+}
+
+StandardErrorReporter::~StandardErrorReporter()
+{
+ delete _impl;
+}
+
+void StandardErrorReporter::startContext(const char *name)
+{
+ _impl->_contexts.push_back(name);
+}
+
+void StandardErrorReporter::add(ErrorType type, const char *reason)
+{
+ incrementCount(type);
+ int indent = (_impl->_prevContext + 1) * 2;
+ if (!_impl->_contexts.empty())
+ {
+ std::vector<std::string>::const_iterator ci;
+ for (ci = _impl->_contexts.begin() + _impl->_prevContext + 1;
+ ci != _impl->_contexts.end(); ++ci)
+ {
+ std::fprintf(stderr, "%*s%s\n", indent, "", ci->c_str());
+ indent += 2;
+ }
+ }
+ _impl->_prevContext = _impl->_contexts.size() - 1;
+ std::fprintf(stderr, "%*s%s: %s\n", indent, "",
+ error_type_names[type], reason);
+}
+
+void StandardErrorReporter::finishContext()
+{
+ _impl->_contexts.pop_back();
+ if (_impl->_prevContext >= static_cast<int>(_impl->_contexts.size()))
+ {
+ _impl->_prevContext = _impl->_contexts.size() - 1;
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for ::gmx::StandardErrorReporter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_errorreporting
+ */
+#ifndef GMX_ERRORREPORTING_STANDARDERRORREPORTER_IMPL_HPP
+#define GMX_ERRORREPORTING_STANDARDERRORREPORTER_IMPL_HPP
+
+#include <string>
+#include <vector>
+
+#include "standarderrorreporter.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Private implementation class for StandardErrorReporter.
+ */
+class StandardErrorReporter::Impl
+{
+ public:
+ //! Initializes a reporter without any context.
+ Impl();
+
+ //! Stack of context strings.
+ std::vector<std::string> _contexts;
+ //! Location of the last error in the context stack.
+ int _prevContext;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares ::gmx::StandardErrorReporter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+#ifndef GMX_ERRORREPORTING_STANDARDERRORREPORTER_H
+#define GMX_ERRORREPORTING_STANDARDERRORREPORTER_H
+
+#include "abstracterrorreporter.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Error reporter that writes error descriptions to standard error.
+ *
+ * \inpublicapi
+ * \ingroup module_errorreporting
+ */
+class StandardErrorReporter : public AbstractErrorReporter
+{
+ public:
+ StandardErrorReporter();
+ virtual ~StandardErrorReporter();
+
+ virtual void startContext(const char *name);
+ virtual void add(ErrorType type, const char *reason);
+ virtual void finishContext();
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \defgroup module_fatalerror Handling of Fatal Errors
+ * \ingroup group_utilitymodules
+ * \brief
+ * Provides functions for handling fatal errors.
+ *
+ * Facilities for handling fatal errors are provided by the fatalerror.h header
+ * file. It provides a set of error codes (the enum ::ErrorCode) that should
+ * be used for return codes in functions. It also provides function fatalError()
+ * for reporting the cause of the error to the user, and convenience macros
+ * ::GMX_ERROR and ::GMX_ERROR_NORET for calling fatalError(). If the reason
+ * string needs formatting, fatalErrorFormatted() is also provided.
+ *
+ * For users of the library, setFatalErrorHandler() is provided to alter the
+ * behavior of fatalError() and fatalErrorFormatted(). fatalError() simply
+ * calls the provided handler, while fatalErrorFormatted() does the formatting
+ * internally and then calls the same handler. The default handler prints the
+ * reason of the error to standard error and aborts the execution.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+/*! \file
+ * \brief
+ * Public API convenience header for fatal error handling.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_fatalerror
+ */
+#ifndef GMX_FATALERROR_H
+#define GMX_FATALERROR_H
+
+#include "fatalerror/fatalerror.h"
+
+#endif
--- /dev/null
+file(GLOB FATALERROR_SOURCES *.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${FATALERROR_SOURCES} PARENT_SCOPE)
+
+set(FATALERROR_PUBLIC_HEADERS
+ fatalerror.h)
+install(FILES ${FATALERROR_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/fatalerror
+ COMPONENT development)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in fatalerror.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_fatalerror
+ */
+#include "gromacs/fatalerror/fatalerror.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+static const char *const error_names[] =
+{
+ "No error",
+ "Out of memory",
+ "File not found",
+ "System I/O error",
+ "Error in user input",
+ "Inconsistency in user input",
+ "Simulation instability detected",
+
+ "Feature not implemented",
+ "Invalid value (bug)",
+ "Invalid call (bug)",
+ "Internal error (bug)",
+ "Range checking error (possible bug)",
+ "Communication error (possible bug)",
+
+ "Unknown error",
+};
+
+namespace gmx
+{
+
+static void standardErrorHandler(int retcode, const char *msg,
+ const char *file, int line)
+{
+ using std::fprintf;
+
+ fprintf(stderr, "\n-------------------------------------------------------\n");
+ fprintf(stderr, "Program %s, %s\n", "TEST", "VERSION 0.1");
+ fprintf(stderr, "In source file %s, line %d\n\n", file, line);
+ if (retcode < 0 || retcode >= eeUnknownError)
+ {
+ retcode = eeUnknownError;
+ }
+ fprintf(stderr, "%s:\n", error_names[retcode]);
+ fprintf(stderr, "%s\n", msg);
+ fprintf(stderr, "For more information and tips for troubleshooting, please check the GROMACS\n"
+ "website at http://www.gromacs.org/Documentation/Errors");
+ fprintf(stderr, "\n-------------------------------------------------------\n");
+ std::exit(1);
+}
+
+static ErrorHandlerFunc error_handler = standardErrorHandler;
+
+ErrorHandlerFunc setFatalErrorHandler(ErrorHandlerFunc handler)
+{
+ // TODO: Acquire a mutex here
+ ErrorHandlerFunc old_handler = error_handler;
+ error_handler = handler;
+ // TODO: Release the mutex here
+ return old_handler;
+}
+
+void fatalError(int retcode, const char *msg, const char *file, int line)
+{
+ // TODO: Acquire a mutex here
+ ErrorHandlerFunc handler = error_handler;
+ // TODO: Release the mutex here
+ if (handler != NULL)
+ {
+ handler(retcode, msg, file, line);
+ }
+}
+
+void fatalErrorFormatted(int retcode, const char *file, int line,
+ const char *fmt, ...)
+{
+ // TODO: Acquire a mutex here
+ ErrorHandlerFunc handler = error_handler;
+ // TODO: Release the mutex here
+ if (handler != NULL)
+ {
+ char errorbuf[10000];
+ va_list va;
+
+ va_start(va, fmt);
+ // FIXME: vsnprintf should really be used here
+ std::vsprintf(errorbuf, fmt, va);
+ va_end(va);
+ handler(retcode, errorbuf, file, line);
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares common return codes and functions for fatal error handling.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_fatalerror
+ */
+#ifndef GMX_FATALERROR_FATALERROR_H
+#define GMX_FATALERROR_FATALERROR_H
+
+namespace gmx
+{
+
+/*! \addtopublicapi
+ * \{
+ */
+
+/*! \brief
+ * Possible error return codes from Gromacs functions.
+ */
+enum ErrorCode
+{
+ //! Zero for successful return.
+ eeOK,
+ //! Not enough memory to complete operation.
+ eeOutOfMemory,
+ //! Provided file could not be opened.
+ eeFileNotFound,
+ //! System I/O error.
+ eeFileIO,
+ //! Invalid user input (could not be understood).
+ eeInvalidInput,
+ //! Invalid user input (conflicting or unsupported settings).
+ eeInconsistentInput,
+ //! Simulation instability detected.
+ eeInstability,
+
+ // Error codes below are for internal error checking; if triggered, they
+ // should indicate a bug in the code.
+ //! Requested feature not yet implemented.
+ eeNotImplemented,
+ //! Input value violates API specification.
+ eeInvalidValue,
+ //! Invalid routine called or wrong calling sequence detected.
+ eeInvalidCall,
+ //! Internal consistency check failed.
+ eeInternalError,
+ //! Range consistency check failed.
+ eeRange,
+ //! Communication consistency check failed.
+ eeCommunication,
+
+ eeUnknownError,
+};
+
+/*! \brief
+ * Callback function pointer type for error handlers.
+ *
+ * \param[in] retcode Code of the error that has occurred.
+ * \param[in] msg More detailed description of the error.
+ * \param[in] file Name of the file where the error occurred.
+ * \param[in] line Line in \p file on which the error occurred.
+ */
+typedef void (*ErrorHandlerFunc)(int retcode, const char *msg,
+ const char *file, int line);
+
+/*! \brief
+ * Sets callback function for handling errors.
+ *
+ * \param[in] handler New error handler function.
+ * \returns Old error handler function.
+ *
+ * The default error handler prints out the location and reason of the error to
+ * stderr, and then calls abort().
+ */
+ErrorHandlerFunc setFatalErrorHandler(ErrorHandlerFunc handler);
+
+/*! \brief
+ * Raises a fatal error.
+ *
+ * \param[in] retcode Error code to raise.
+ * \param[in] msg More detailed description of the error.
+ * \param[in] file Name of the source file where the error occurred.
+ * \param[in] line Line in \p file on which the error occurred.
+ */
+void fatalError(int retcode, const char *msg, const char *file, int line);
+/*! \brief
+ * Raises an error with a formatted message.
+ *
+ * Use like this:
+ * \code
+::gmx::fatalErrorFormatted(::gmx::eeInvalidInput, GMX_ERRORLOC,
+ "Invalid command-line argument: %s", argname);
+ * \endcode
+ *
+ * \param[in] retcode Error code to raise.
+ * \param[in] file Name of the source file where the error occurred.
+ * \param[in] line Line in \p file on which the error occurred.
+ * \param[in] fmt printf format string.
+ */
+void fatalErrorFormatted(int retcode, const char *file, int line,
+ const char *fmt, ...);
+
+/*! \brief
+ * Helper macro for fatalErrorFormatted().
+ *
+ * \see fatalErrorFormatted()
+ */
+#define GMX_ERRORLOC __FILE__, __LINE__
+
+/*! \brief
+ * Macro for raising an error and returning from a function.
+ *
+ * The function should return \c int ; if it doesn't, use GMX_ERROR_NORET.
+ */
+#define GMX_ERROR(retcode, msg) \
+ do { \
+ int _rc_internal = (retcode); \
+ ::gmx::fatalError(_rc_internal, msg, __FILE__, __LINE__); \
+ return _rc_internal; \
+ } while (0)
+
+/*! \brief
+ * Macro for raising an error in a function that does not return \c int.
+ *
+ * \see GMX_ERROR
+ */
+#define GMX_ERROR_NORET(retcode, msg) \
+ ::gmx::fatalError(retcode, msg, __FILE__, __LINE__)
+
+/*! \def GMX_ERROR_DEBUG
+ * \brief
+ * Macro for raising a debugging error and returning from a function.
+ *
+ * If NDEBUG is defined, this macro only returns the given error code.
+ * If it is not defined, behaves exactly like GMX_ERROR.
+ *
+ * The function should return \c int ; if it doesn't, use GMX_ERROR_DEBUG_NORET.
+ */
+/*! \def GMX_ERROR_DEBUG_NORET
+ * \brief
+ * Macro for raising a debugging error in a function that does not return \c int.
+ *
+ * If NDEBUG is defined, this macro does nothing. If it is not defined,
+ * behaves exactly like GMX_ERROR_NORET.
+ */
+#ifdef NDEBUG
+#define GMX_ERROR_DEBUG(retcode, msg) \
+ return (retcode)
+#define GMX_ERROR_DEBUG_NORET(retcode, msg)
+#else
+#define GMX_ERROR_DEBUG(retcode, msg) \
+ do { \
+ int _rc_internal = (retcode); \
+ ::gmx::fatalError(_rc_internal, msg, __FILE__, __LINE__); \
+ return _rc_internal; \
+ } while (0)
+#define GMX_ERROR_DEBUG_NORET(retcode, msg) \
+ ::gmx::fatalError(retcode, msg, __FILE__, __LINE__)
+#endif
+
+/*!\}*/
+
+} // namespace gmx
+
+#endif
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+# The nonbonded directory contains subdirectories that are only
+# conditionally built, so we cannot use a GLOB_RECURSE here.
+file(GLOB GMXLIB_SOURCES *.c
+ statistics/*.c nonbonded/*.c nonbonded/nb_kernel_c/*.c)
+
+if(GMX_DOUBLE)
+ set(SSETYPE sse2)
+else()
+ set(SSETYPE sse)
+endif()
+
+if(GMX_IA32_ASM)
+ file(GLOB GMX_SSEKERNEL_C_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*.c)
+ if(GMX_ASM_USEASM-NASM)
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*intel_syntax*.s)
+ else()
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*${SSETYPE}.s nonbonded/nb_kernel_ia32_${SSETYPE}/*asm.s)
+ endif()
+endif(GMX_IA32_ASM)
+
+if(GMX_X86_64_ASM)
+ file(GLOB GMX_SSEKERNEL_C_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*.c)
+ if(GMX_ASM_USEASM-NASM)
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*intel_syntax*.s)
+ else()
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*${SSETYPE}.s nonbonded/nb_kernel_x86_64_${SSETYPE}/*asm.s)
+ endif()
+endif(GMX_X86_64_ASM)
+
+if(GMX_FORTRAN)
+ if (GMX_DOUBLE)
+ file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_f77_double/*.[cf])
+ else(GMX_DOUBLE)
+ file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_f77_single/*.[cf])
+ endif(GMX_DOUBLE)
+endif(GMX_FORTRAN)
+
+if(GMX_POWER6)
+ file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_power6/*.[cF])
+endif(GMX_POWER6)
+
+if(GMX_BLUEGENE)
+ file(GLOB GMX_BLUEGENE_C_SRC nonbonded/nb_kernel_bluegene/*.c)
+endif(GMX_BLUEGENE)
+
+if(GMX_PPC_ALTIVEC)
+ file(GLOB GMX_PPC_ALTIVEC_SRC nonbonded/nb_kernel_ppc_altivec/*.c)
+endif(GMX_PPC_ALTIVEC)
+
+if(NOT GMX_EXTERNAL_BLAS)
+ file(GLOB BLAS_SOURCES gmx_blas/*.c)
+endif(NOT GMX_EXTERNAL_BLAS)
+
+if(NOT GMX_EXTERNAL_LAPACK)
+ file(GLOB LAPACK_SOURCES gmx_lapack/*.c)
+endif(NOT GMX_EXTERNAL_LAPACK)
+
+# This would be the standard way to include thread_mpi, but we want libgmx
+# to link the functions directly
+#if(GMX_THREADS)
+# add_subdirectory(thread_mpi)
+#endif(GMX_THREADS)
+#target_link_libraries(gmx ${GMX_EXTRA_LIBRARIES} ${THREAD_MPI_LIB})
+
+# Files called xxx_test.c are test drivers with a main() function for module xxx.c,
+# so they should not be included in the library
+file(GLOB_RECURSE NOT_GMXLIB_SOURCES *_test.c *\#*)
+list(REMOVE_ITEM GMXLIB_SOURCES ${NOT_GMXLIB_SOURCES})
+
+# An ugly hack to get absolute paths...
+file(GLOB THREAD_MPI_SOURCES ${THREAD_MPI_SRC})
+
+set(GMX_SSEKERNEL_ASM_SRC ${GMX_SSEKERNEL_ASM_SRC} PARENT_SCOPE)
+set(GMXLIB_SOURCES ${GMXLIB_SOURCES} ${BLAS_SOURCES} ${LAPACK_SOURCES}
+ ${GMX_SSEKERNEL_C_SRC} ${FORTRAN_SOURCES}
+ ${GMX_BLUEGENE_C_SRC} ${GMX_PPC_ALTIVEC_SRC} ${THREAD_MPI_SOURCES}
+ PARENT_SCOPE)
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+/* This file is completely threadsafe - keep it that way! */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sysstuff.h>
+#include <string.h>
+#include "typedefs.h"
+#include "main.h"
+#include "mvdata.h"
+#include "network.h"
+#include "smalloc.h"
+#include "gmx_fatal.h"
+#include "symtab.h"
+#include "vec.h"
+#include "tgroup.h"
+
+#define block_bc(cr, d) gmx_bcast( sizeof(d), &(d),(cr))
+/* Probably the test for (nr) > 0 in the next macro is only needed
+ * on BlueGene(/L), where IBM's MPI_Bcast will segfault after
+ * dereferencing a null pointer, even when no data is to be transferred. */
+#define nblock_bc(cr,nr,d) { if ((nr) > 0) gmx_bcast((nr)*sizeof((d)[0]), (d),(cr)); }
+#define snew_bc(cr,d,nr) { if (!MASTER(cr)) snew((d),(nr)); }
+/* Dirty macro with bAlloc not as an argument */
+#define nblock_abc(cr,nr,d) { if (bAlloc) snew((d),(nr)); nblock_bc(cr,(nr),(d)); }
+
+static void bc_string(const t_commrec *cr,t_symtab *symtab,char ***s)
+{
+ int handle;
+
+ if (MASTER(cr)) {
+ handle = lookup_symtab(symtab,*s);
+ }
+ block_bc(cr,handle);
+ if (!MASTER(cr)) {
+ *s = get_symtab_handle(symtab,handle);
+ }
+}
+
+static void bc_strings(const t_commrec *cr,t_symtab *symtab,int nr,char ****nm)
+{
+ int i;
+ int *handle;
+ char ***NM;
+
+ snew(handle,nr);
+ if (MASTER(cr)) {
+ NM = *nm;
+ for(i=0; (i<nr); i++)
+ handle[i] = lookup_symtab(symtab,NM[i]);
+ }
+ nblock_bc(cr,nr,handle);
+
+ if (!MASTER(cr)) {
+ snew_bc(cr,*nm,nr);
+ NM = *nm;
+ for (i=0; (i<nr); i++)
+ (*nm)[i] = get_symtab_handle(symtab,handle[i]);
+ }
+ sfree(handle);
+}
+
+static void bc_strings_resinfo(const t_commrec *cr,t_symtab *symtab,
+ int nr,t_resinfo *resinfo)
+{
+ int i;
+ int *handle;
+
+ snew(handle,nr);
+ if (MASTER(cr)) {
+ for(i=0; (i<nr); i++)
+ handle[i] = lookup_symtab(symtab,resinfo[i].name);
+ }
+ nblock_bc(cr,nr,handle);
+
+ if (!MASTER(cr)) {
+ for (i=0; (i<nr); i++)
+ resinfo[i].name = get_symtab_handle(symtab,handle[i]);
+ }
+ sfree(handle);
+}
+
+static void bc_symtab(const t_commrec *cr,t_symtab *symtab)
+{
+ int i,nr,len;
+ t_symbuf *symbuf;
+
+ block_bc(cr,symtab->nr);
+ nr = symtab->nr;
+ snew_bc(cr,symtab->symbuf,1);
+ symbuf = symtab->symbuf;
+ symbuf->bufsize = nr;
+ snew_bc(cr,symbuf->buf,nr);
+ for (i=0; i<nr; i++) {
+ if (MASTER(cr))
+ len = strlen(symbuf->buf[i]) + 1;
+ block_bc(cr,len);
+ snew_bc(cr,symbuf->buf[i],len);
+ nblock_bc(cr,len,symbuf->buf[i]);
+ }
+}
+
+static void bc_block(const t_commrec *cr,t_block *block)
+{
+ block_bc(cr,block->nr);
+ snew_bc(cr,block->index,block->nr+1);
+ nblock_bc(cr,block->nr+1,block->index);
+}
+
+static void bc_blocka(const t_commrec *cr,t_blocka *block)
+{
+ block_bc(cr,block->nr);
+ snew_bc(cr,block->index,block->nr+1);
+ nblock_bc(cr,block->nr+1,block->index);
+ block_bc(cr,block->nra);
+ if (block->nra) {
+ snew_bc(cr,block->a,block->nra);
+ nblock_bc(cr,block->nra,block->a);
+ }
+}
+
+static void bc_grps(const t_commrec *cr,t_grps grps[])
+{
+ int i;
+
+ for(i=0; (i<egcNR); i++) {
+ block_bc(cr,grps[i].nr);
+ snew_bc(cr,grps[i].nm_ind,grps[i].nr);
+ nblock_bc(cr,grps[i].nr,grps[i].nm_ind);
+ }
+}
+
+static void bc_atoms(const t_commrec *cr,t_symtab *symtab,t_atoms *atoms)
+{
+ int dummy;
+
+ block_bc(cr,atoms->nr);
+ snew_bc(cr,atoms->atom,atoms->nr);
+ nblock_bc(cr,atoms->nr,atoms->atom);
+ bc_strings(cr,symtab,atoms->nr,&atoms->atomname);
+ block_bc(cr,atoms->nres);
+ snew_bc(cr,atoms->resinfo,atoms->nres);
+ nblock_bc(cr,atoms->nres,atoms->resinfo);
+ bc_strings_resinfo(cr,symtab,atoms->nres,atoms->resinfo);
+ /* QMMM requires atomtypes to be known on all nodes as well */
+ bc_strings(cr,symtab,atoms->nr,&atoms->atomtype);
+ bc_strings(cr,symtab,atoms->nr,&atoms->atomtypeB);
+}
+
+static void bc_groups(const t_commrec *cr,t_symtab *symtab,
+ int natoms,gmx_groups_t *groups)
+{
+ int dummy;
+ int g,n;
+
+ bc_grps(cr,groups->grps);
+ block_bc(cr,groups->ngrpname);
+ bc_strings(cr,symtab,groups->ngrpname,&groups->grpname);
+ for(g=0; g<egcNR; g++) {
+ if (MASTER(cr)) {
+ if (groups->grpnr[g]) {
+ n = natoms;
+ } else {
+ n = 0;
+ }
+ }
+ block_bc(cr,n);
+ if (n == 0) {
+ groups->grpnr[g] = NULL;
+ } else {
+ snew_bc(cr,groups->grpnr[g],n);
+ nblock_bc(cr,n,groups->grpnr[g]);
+ }
+ }
+ if (debug) fprintf(debug,"after bc_groups\n");
+}
+
+void bcast_state_setup(const t_commrec *cr,t_state *state)
+{
+ block_bc(cr,state->natoms);
+ block_bc(cr,state->ngtc);
+ block_bc(cr,state->nnhpres);
+ block_bc(cr,state->nhchainlength);
+ block_bc(cr,state->nrng);
+ block_bc(cr,state->nrngi);
+ block_bc(cr,state->flags);
+}
+
+void bcast_state(const t_commrec *cr,t_state *state,gmx_bool bAlloc)
+{
+ int i,nnht,nnhtp;
+
+ bcast_state_setup(cr,state);
+
+ nnht = (state->ngtc)*(state->nhchainlength);
+ nnhtp = (state->nnhpres)*(state->nhchainlength);
+
+ if (MASTER(cr)) {
+ bAlloc = FALSE;
+ }
+ if (bAlloc) {
+ state->nalloc = state->natoms;
+ }
+ for(i=0; i<estNR; i++) {
+ if (state->flags & (1<<i)) {
+ switch (i) {
+ case estLAMBDA: block_bc(cr,state->lambda); break;
+ case estBOX: block_bc(cr,state->box); break;
+ case estBOX_REL: block_bc(cr,state->box_rel); break;
+ case estBOXV: block_bc(cr,state->boxv); break;
+ case estPRES_PREV: block_bc(cr,state->pres_prev); break;
+ case estSVIR_PREV: block_bc(cr,state->svir_prev); break;
+ case estFVIR_PREV: block_bc(cr,state->fvir_prev); break;
+ case estNH_XI: nblock_abc(cr,nnht,state->nosehoover_xi); break;
+ case estNH_VXI: nblock_abc(cr,nnht,state->nosehoover_vxi); break;
+ case estNHPRES_XI: nblock_abc(cr,nnhtp,state->nhpres_xi); break;
+ case estNHPRES_VXI: nblock_abc(cr,nnhtp,state->nhpres_vxi); break;
+ case estTC_INT: nblock_abc(cr,state->ngtc,state->therm_integral); break;
+ case estVETA: block_bc(cr,state->veta); break;
+ case estVOL0: block_bc(cr,state->vol0); break;
+ case estX: nblock_abc(cr,state->natoms,state->x); break;
+ case estV: nblock_abc(cr,state->natoms,state->v); break;
+ case estSDX: nblock_abc(cr,state->natoms,state->sd_X); break;
+ case estCGP: nblock_abc(cr,state->natoms,state->cg_p); break;
+ case estLD_RNG: if(state->nrngi == 1) nblock_abc(cr,state->nrng,state->ld_rng); break;
+ case estLD_RNGI: if(state->nrngi == 1) nblock_abc(cr,state->nrngi,state->ld_rngi); break;
+ case estDISRE_INITF: block_bc(cr,state->hist.disre_initf); break;
+ case estDISRE_RM3TAV:
+ block_bc(cr,state->hist.ndisrepairs);
+ nblock_abc(cr,state->hist.ndisrepairs,state->hist.disre_rm3tav);
+ break;
+ case estORIRE_INITF: block_bc(cr,state->hist.orire_initf); break;
+ case estORIRE_DTAV:
+ block_bc(cr,state->hist.norire_Dtav);
+ nblock_abc(cr,state->hist.norire_Dtav,state->hist.orire_Dtav);
+ break;
+ default:
+ gmx_fatal(FARGS,
+ "Communication is not implemented for %s in bcast_state",
+ est_names[i]);
+ }
+ }
+ }
+}
+
+static void bc_ilists(const t_commrec *cr,t_ilist *ilist)
+{
+ int ftype;
+
+ /* Here we only communicate the non-zero length ilists */
+ if (MASTER(cr)) {
+ for(ftype=0; ftype<F_NRE; ftype++) {
+ if (ilist[ftype].nr > 0) {
+ block_bc(cr,ftype);
+ block_bc(cr,ilist[ftype].nr);
+ nblock_bc(cr,ilist[ftype].nr,ilist[ftype].iatoms);
+ }
+ }
+ ftype = -1;
+ block_bc(cr,ftype);
+ } else {
+ for(ftype=0; ftype<F_NRE; ftype++) {
+ ilist[ftype].nr = 0;
+ }
+ do {
+ block_bc(cr,ftype);
+ if (ftype >= 0) {
+ block_bc(cr,ilist[ftype].nr);
+ snew_bc(cr,ilist[ftype].iatoms,ilist[ftype].nr);
+ nblock_bc(cr,ilist[ftype].nr,ilist[ftype].iatoms);
+ }
+ } while (ftype >= 0);
+ }
+
+ if (debug) fprintf(debug,"after bc_ilists\n");
+}
+
+static void bc_idef(const t_commrec *cr,t_idef *idef)
+{
+ block_bc(cr,idef->ntypes);
+ block_bc(cr,idef->atnr);
+ snew_bc(cr,idef->functype,idef->ntypes);
+ snew_bc(cr,idef->iparams,idef->ntypes);
+ nblock_bc(cr,idef->ntypes,idef->functype);
+ nblock_bc(cr,idef->ntypes,idef->iparams);
+ block_bc(cr,idef->fudgeQQ);
+ bc_ilists(cr,idef->il);
+ block_bc(cr,idef->ilsort);
+}
+
+static void bc_cmap(const t_commrec *cr, gmx_cmap_t *cmap_grid)
+{
+ int i,j,nelem,ngrid;
+
+ block_bc(cr,cmap_grid->ngrid);
+ block_bc(cr,cmap_grid->grid_spacing);
+
+ ngrid = cmap_grid->ngrid;
+ nelem = cmap_grid->grid_spacing * cmap_grid->grid_spacing;
+
+ if(ngrid>0)
+ {
+ snew_bc(cr,cmap_grid->cmapdata,ngrid);
+
+ for(i=0;i<ngrid;i++)
+ {
+ snew_bc(cr,cmap_grid->cmapdata[i].cmap,4*nelem);
+ nblock_bc(cr,4*nelem,cmap_grid->cmapdata[i].cmap);
+ }
+ }
+}
+
+static void bc_ffparams(const t_commrec *cr,gmx_ffparams_t *ffp)
+{
+ int i;
+
+ block_bc(cr,ffp->ntypes);
+ block_bc(cr,ffp->atnr);
+ snew_bc(cr,ffp->functype,ffp->ntypes);
+ snew_bc(cr,ffp->iparams,ffp->ntypes);
+ nblock_bc(cr,ffp->ntypes,ffp->functype);
+ nblock_bc(cr,ffp->ntypes,ffp->iparams);
+ block_bc(cr,ffp->reppow);
+ block_bc(cr,ffp->fudgeQQ);
+ bc_cmap(cr,&ffp->cmap_grid);
+}
+
+static void bc_grpopts(const t_commrec *cr,t_grpopts *g)
+{
+ int i,n;
+
+ block_bc(cr,g->ngtc);
+ block_bc(cr,g->ngacc);
+ block_bc(cr,g->ngfrz);
+ block_bc(cr,g->ngener);
+ snew_bc(cr,g->nrdf,g->ngtc);
+ snew_bc(cr,g->tau_t,g->ngtc);
+ snew_bc(cr,g->ref_t,g->ngtc);
+ snew_bc(cr,g->acc,g->ngacc);
+ snew_bc(cr,g->nFreeze,g->ngfrz);
+ snew_bc(cr,g->egp_flags,g->ngener*g->ngener);
+
+ nblock_bc(cr,g->ngtc,g->nrdf);
+ nblock_bc(cr,g->ngtc,g->tau_t);
+ nblock_bc(cr,g->ngtc,g->ref_t);
+ nblock_bc(cr,g->ngacc,g->acc);
+ nblock_bc(cr,g->ngfrz,g->nFreeze);
+ nblock_bc(cr,g->ngener*g->ngener,g->egp_flags);
+ snew_bc(cr,g->annealing,g->ngtc);
+ snew_bc(cr,g->anneal_npoints,g->ngtc);
+ snew_bc(cr,g->anneal_time,g->ngtc);
+ snew_bc(cr,g->anneal_temp,g->ngtc);
+ nblock_bc(cr,g->ngtc,g->annealing);
+ nblock_bc(cr,g->ngtc,g->anneal_npoints);
+ for(i=0;(i<g->ngtc); i++) {
+ n = g->anneal_npoints[i];
+ if (n > 0) {
+ snew_bc(cr,g->anneal_time[i],n);
+ snew_bc(cr,g->anneal_temp[i],n);
+ nblock_bc(cr,n,g->anneal_time[i]);
+ nblock_bc(cr,n,g->anneal_temp[i]);
+ }
+ }
+
+ /* QMMM stuff, see inputrec */
+ block_bc(cr,g->ngQM);
+ snew_bc(cr,g->QMmethod,g->ngQM);
+ snew_bc(cr,g->QMbasis,g->ngQM);
+ snew_bc(cr,g->QMcharge,g->ngQM);
+ snew_bc(cr,g->QMmult,g->ngQM);
+ snew_bc(cr,g->bSH,g->ngQM);
+ snew_bc(cr,g->CASorbitals,g->ngQM);
+ snew_bc(cr,g->CASelectrons,g->ngQM);
+ snew_bc(cr,g->SAon,g->ngQM);
+ snew_bc(cr,g->SAoff,g->ngQM);
+ snew_bc(cr,g->SAsteps,g->ngQM);
+
+ if (g->ngQM)
+ {
+ nblock_bc(cr,g->ngQM,g->QMmethod);
+ nblock_bc(cr,g->ngQM,g->QMbasis);
+ nblock_bc(cr,g->ngQM,g->QMcharge);
+ nblock_bc(cr,g->ngQM,g->QMmult);
+ nblock_bc(cr,g->ngQM,g->bSH);
+ nblock_bc(cr,g->ngQM,g->CASorbitals);
+ nblock_bc(cr,g->ngQM,g->CASelectrons);
+ nblock_bc(cr,g->ngQM,g->SAon);
+ nblock_bc(cr,g->ngQM,g->SAoff);
+ nblock_bc(cr,g->ngQM,g->SAsteps);
+ /* end of QMMM stuff */
+ }
+}
+
+static void bc_cosines(const t_commrec *cr,t_cosines *cs)
+{
+ block_bc(cr,cs->n);
+ snew_bc(cr,cs->a,cs->n);
+ snew_bc(cr,cs->phi,cs->n);
+ if (cs->n > 0) {
+ nblock_bc(cr,cs->n,cs->a);
+ nblock_bc(cr,cs->n,cs->phi);
+ }
+}
+
+static void bc_pullgrp(const t_commrec *cr,t_pullgrp *pgrp)
+{
+ block_bc(cr,*pgrp);
+ if (pgrp->nat > 0) {
+ snew_bc(cr,pgrp->ind,pgrp->nat);
+ nblock_bc(cr,pgrp->nat,pgrp->ind);
+ }
+ if (pgrp->nweight > 0) {
+ snew_bc(cr,pgrp->weight,pgrp->nweight);
+ nblock_bc(cr,pgrp->nweight,pgrp->weight);
+ }
+}
+
+static void bc_pull(const t_commrec *cr,t_pull *pull)
+{
+ int g;
+
+ block_bc(cr,*pull);
+ snew_bc(cr,pull->grp,pull->ngrp+1);
+ for(g=0; g<pull->ngrp+1; g++)
+ {
+ bc_pullgrp(cr,&pull->grp[g]);
+ }
+}
+
+static void bc_rotgrp(const t_commrec *cr,t_rotgrp *rotg)
+{
+ block_bc(cr,*rotg);
+ if (rotg->nat > 0) {
+ snew_bc(cr,rotg->ind,rotg->nat);
+ nblock_bc(cr,rotg->nat,rotg->ind);
+ snew_bc(cr,rotg->x_ref,rotg->nat);
+ nblock_bc(cr,rotg->nat,rotg->x_ref);
+ }
+}
+
+static void bc_rot(const t_commrec *cr,t_rot *rot)
+{
+ int g;
+
+ block_bc(cr,*rot);
+ snew_bc(cr,rot->grp,rot->ngrp);
+ for(g=0; g<rot->ngrp; g++)
+ bc_rotgrp(cr,&rot->grp[g]);
+}
+
+static void bc_inputrec(const t_commrec *cr,t_inputrec *inputrec)
+{
+ gmx_bool bAlloc=TRUE;
+ int i;
+
+ block_bc(cr,*inputrec);
+ snew_bc(cr,inputrec->flambda,inputrec->n_flambda);
+ nblock_bc(cr,inputrec->n_flambda,inputrec->flambda);
+ bc_grpopts(cr,&(inputrec->opts));
+ if (inputrec->ePull != epullNO) {
+ snew_bc(cr,inputrec->pull,1);
+ bc_pull(cr,inputrec->pull);
+ }
+ if (inputrec->bRot) {
+ snew_bc(cr,inputrec->rot,1);
+ bc_rot(cr,inputrec->rot);
+ }
+ for(i=0; (i<DIM); i++) {
+ bc_cosines(cr,&(inputrec->ex[i]));
+ bc_cosines(cr,&(inputrec->et[i]));
+ }
+}
+
+static void bc_moltype(const t_commrec *cr,t_symtab *symtab,
+ gmx_moltype_t *moltype)
+{
+ bc_string(cr,symtab,&moltype->name);
+ bc_atoms(cr,symtab,&moltype->atoms);
+ if (debug) fprintf(debug,"after bc_atoms\n");
+
+ bc_ilists(cr,moltype->ilist);
+ bc_block(cr,&moltype->cgs);
+ bc_blocka(cr,&moltype->excls);
+}
+
+static void bc_molblock(const t_commrec *cr,gmx_molblock_t *molb)
+{
+ gmx_bool bAlloc=TRUE;
+
+ block_bc(cr,molb->type);
+ block_bc(cr,molb->nmol);
+ block_bc(cr,molb->natoms_mol);
+ block_bc(cr,molb->nposres_xA);
+ if (molb->nposres_xA > 0) {
+ snew_bc(cr,molb->posres_xA,molb->nposres_xA);
+ nblock_bc(cr,molb->nposres_xA*DIM,molb->posres_xA[0]);
+ }
+ block_bc(cr,molb->nposres_xB);
+ if (molb->nposres_xB > 0) {
+ snew_bc(cr,molb->posres_xB,molb->nposres_xB);
+ nblock_bc(cr,molb->nposres_xB*DIM,molb->posres_xB[0]);
+ }
+ if (debug) fprintf(debug,"after bc_molblock\n");
+}
+
+static void bc_atomtypes(const t_commrec *cr, t_atomtypes *atomtypes)
+{
+ int nr;
+
+ block_bc(cr,atomtypes->nr);
+
+ nr = atomtypes->nr;
+
+ snew_bc(cr,atomtypes->radius,nr);
+ snew_bc(cr,atomtypes->vol,nr);
+ snew_bc(cr,atomtypes->surftens,nr);
+ snew_bc(cr,atomtypes->gb_radius,nr);
+ snew_bc(cr,atomtypes->S_hct,nr);
+
+ nblock_bc(cr,nr,atomtypes->radius);
+ nblock_bc(cr,nr,atomtypes->vol);
+ nblock_bc(cr,nr,atomtypes->surftens);
+ nblock_bc(cr,nr,atomtypes->gb_radius);
+ nblock_bc(cr,nr,atomtypes->S_hct);
+}
+
+
+void bcast_ir_mtop(const t_commrec *cr,t_inputrec *inputrec,gmx_mtop_t *mtop)
+{
+ int i;
+ if (debug) fprintf(debug,"in bc_data\n");
+ bc_inputrec(cr,inputrec);
+ if (debug) fprintf(debug,"after bc_inputrec\n");
+ bc_symtab(cr,&mtop->symtab);
+ if (debug) fprintf(debug,"after bc_symtab\n");
+ bc_string(cr,&mtop->symtab,&mtop->name);
+ if (debug) fprintf(debug,"after bc_name\n");
+
+ bc_ffparams(cr,&mtop->ffparams);
+
+ block_bc(cr,mtop->nmoltype);
+ snew_bc(cr,mtop->moltype,mtop->nmoltype);
+ for(i=0; i<mtop->nmoltype; i++) {
+ bc_moltype(cr,&mtop->symtab,&mtop->moltype[i]);
+ }
+
+ block_bc(cr,mtop->nmolblock);
+ snew_bc(cr,mtop->molblock,mtop->nmolblock);
+ for(i=0; i<mtop->nmolblock; i++) {
+ bc_molblock(cr,&mtop->molblock[i]);
+ }
+
+ block_bc(cr,mtop->natoms);
+
+ bc_atomtypes(cr,&mtop->atomtypes);
+
+ bc_block(cr,&mtop->mols);
+ bc_groups(cr,&mtop->symtab,mtop->natoms,&mtop->groups);
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "typedefs.h"
+#include "names.h"
+
+/* note: these arrays should correspond to enums in include/types/enums.h */
+
+const char *epbc_names[epbcNR+1]=
+{
+ "xyz", "no", "xy", "screw", NULL
+};
+
+const char *ens_names[ensNR+1]=
+{
+ "Grid","Simple", NULL
+};
+
+const char *ei_names[eiNR+1]=
+{
+ "md", "steep", "cg", "bd", "sd", "nm", "l-bfgs", "tpi", "tpic", "sd1", "md-vv", "md-vv-avek",NULL
+};
+
+const char *bool_names[BOOL_NR+1]=
+{
+ "FALSE","TRUE", NULL
+};
+
+const char *yesno_names[BOOL_NR+1]=
+{
+ "no","yes", NULL
+};
+
+const char *ptype_str[eptNR+1] = {
+ "Atom", "Nucleus", "Shell", "Bond", "VSite", NULL
+};
+
+const char *eel_names[eelNR+1] = {
+ "Cut-off", "Reaction-Field", "Generalized-Reaction-Field",
+ "PME", "Ewald", "PPPM", "Poisson", "Switch", "Shift", "User",
+ "Generalized-Born", "Reaction-Field-nec", "Encad-shift",
+ "PME-User", "PME-Switch", "PME-User-Switch",
+ "Reaction-Field-zero", NULL
+};
+
+const char *eewg_names[eewgNR+1] = {
+ "3d", "3dc", NULL
+};
+
+const char *evdw_names[evdwNR+1] = {
+ "Cut-off", "Switch", "Shift", "User", "Encad-shift", NULL
+};
+
+const char *econstr_names[econtNR+1] = {
+ "Lincs", "Shake", NULL
+};
+
+const char *egrp_nm[egNR+1] = {
+ "Coul-SR","LJ-SR","Buck-SR", "Coul-LR", "LJ-LR", "Buck-LR",
+ "Coul-14", "LJ-14", NULL
+};
+
+const char *etcoupl_names[etcNR+1] = {
+ "No", "Berendsen", "Nose-Hoover", "yes", "Andersen", "Andersen-interval", "V-rescale", NULL
+}; /* yes is alias for berendsen */
+
+const char *epcoupl_names[epcNR+1] = {
+ "No", "Berendsen", "Parrinello-Rahman", "Isotropic", "MTTK", NULL
+}; /* isotropic is alias for berendsen */
+
+const char *epcoupltype_names[epctNR+1] = {
+ "Isotropic", "Semiisotropic", "Anisotropic", "Surface-Tension", NULL
+};
+
+const char *erefscaling_names[erscNR+1] = {
+ "No", "All", "COM", NULL
+};
+
+const char *edisre_names[edrNR+1] = {
+ "No", "Simple", "Ensemble", NULL
+};
+
+const char *edisreweighting_names[edrwNR+1] = {
+ "Conservative", "Equal", NULL
+};
+
+const char *enbf_names[eNBF_NR+1] = {
+ "", "LJ", "Buckingham", NULL
+};
+
+const char *ecomb_names[eCOMB_NR+1] = {
+ "", "Geometric", "Arithmetic", "GeomSigEps", NULL
+};
+
+const char *gtypes[egcNR+1] = {
+ "T-Coupling", "Energy Mon.", "Acceleration", "Freeze",
+ "User1", "User2", "VCM", "XTC", "Or. Res. Fit", "QMMM", NULL
+};
+
+const char *efep_names[efepNR+1] = {
+ "no", "yes", NULL
+};
+
+const char *separate_dhdl_file_names[sepdhdlfileNR+1] = {
+ "yes", "no", NULL
+};
+
+const char *dhdl_derivatives_names[dhdlderivativesNR+1] = {
+ "yes", "no", NULL
+};
+
+const char *esol_names[esolNR+1] = {
+ "No", "SPC", "TIP4p", NULL
+};
+
+const char *enlist_names[enlistNR+1] = {
+ "Atom-Atom", "SPC-Atom", "SPC-SPC", "TIP4p-Atom", "TIP4p-TIP4p", "CG-CG", NULL
+};
+
+const char *edispc_names[edispcNR+1] = {
+ "No", "EnerPres", "Ener", "AllEnerPres", "AllEner", NULL
+};
+
+const char *ecm_names[ecmNR+1] = {
+ "Linear", "Angular", "None", NULL
+};
+
+const char *eann_names[eannNR+1] = {
+ "No", "Single", "Periodic", NULL
+};
+
+const char *eis_names[eisNR+1] = {
+ "No", "GBSA", NULL
+};
+
+const char *egb_names[egbNR+1] = {
+ "Still", "HCT", "OBC", NULL
+};
+
+const char *esa_names[esaNR+1] = {
+ "Ace-approximation", "None", "Still", NULL
+};
+
+const char *ewt_names[ewtNR+1] = {
+ "9-3", "10-4", "table", "12-6", NULL
+};
+
+const char *epull_names[epullNR+1] = {
+ "no", "umbrella", "constraint", "constant_force", NULL
+};
+
+const char *epullg_names[epullgNR+1] = {
+ "distance", "direction", "cylinder", "position", "direction_periodic", NULL
+};
+
+const char *erotg_names[erotgNR+1] = {
+ "iso", "iso-pf", "pm", "pm-pf", "rm", "rm-pf", "rm2", "rm2-pf", "flex", "flex-t", "flex2", "flex2-t", NULL
+};
+
+const char *erotg_fitnames[erotgFitNR+1] = {
+ "rmsd", "norm", NULL
+};
+
+const char *eQMmethod_names[eQMmethodNR+1] = {
+ "AM1", "PM3", "RHF",
+ "UHF", "DFT", "B3LYP", "MP2", "CASSCF","B3LYPLAN",
+ "DIRECT", NULL
+};
+
+const char *eQMbasis_names[eQMbasisNR+1] = {
+ "STO3G", "STO-3G", "3-21G",
+ "3-21G*", "3-21+G*", "6-21G",
+ "6-31G", "6-31G*", "6-31+G*",
+ "6-311G", NULL
+};
+
+const char *eQMMMscheme_names[eQMMMschemeNR+1] = {
+ "normal", "ONIOM", NULL
+};
+
+const char *eMultentOpt_names[eMultentOptNR+1] = {
+ "multiple_entries", "no", "use_last", NULL
+};
+
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include "gmx_fatal.h"
+#include "main.h"
+#include "smalloc.h"
+#include "network.h"
+#include "copyrite.h"
+#include "statutil.h"
+#include "ctype.h"
+#include "macros.h"
+
+#ifdef GMX_LIB_MPI
+#include <mpi.h>
+#endif
+
+#ifdef GMX_THREADS
+#include "tmpi.h"
+#endif
+
+#include "mpelogging.h"
+
+/* The source code in this file should be thread-safe.
+ Please keep it that way. */
+
+gmx_bool gmx_mpi_initialized(void)
+{
+ int n;
+#ifndef GMX_MPI
+ return 0;
+#else
+ MPI_Initialized(&n);
+
+ return n;
+#endif
+}
+
+int gmx_setup(int *argc,char **argv,int *nnodes)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_setup");
+ return 0;
+#else
+ char buf[256];
+ int resultlen; /* actual length of node name */
+ int i,flag;
+ int mpi_num_nodes;
+ int mpi_my_rank;
+ char mpi_hostname[MPI_MAX_PROCESSOR_NAME];
+
+ /* Call the MPI routines */
+#ifdef GMX_LIB_MPI
+#ifdef GMX_FAHCORE
+ (void) fah_MPI_Init(argc,&argv);
+#else
+ (void) MPI_Init(argc,&argv);
+#endif
+#endif
+ (void) MPI_Comm_size( MPI_COMM_WORLD, &mpi_num_nodes );
+ (void) MPI_Comm_rank( MPI_COMM_WORLD, &mpi_my_rank );
+ (void) MPI_Get_processor_name( mpi_hostname, &resultlen );
+
+
+#ifdef USE_MPE
+ /* MPE logging routines. Get event IDs from MPE: */
+ /* General events */
+ ev_timestep1 = MPE_Log_get_event_number( );
+ ev_timestep2 = MPE_Log_get_event_number( );
+ ev_force_start = MPE_Log_get_event_number( );
+ ev_force_finish = MPE_Log_get_event_number( );
+ ev_do_fnbf_start = MPE_Log_get_event_number( );
+ ev_do_fnbf_finish = MPE_Log_get_event_number( );
+ ev_ns_start = MPE_Log_get_event_number( );
+ ev_ns_finish = MPE_Log_get_event_number( );
+ ev_calc_bonds_start = MPE_Log_get_event_number( );
+ ev_calc_bonds_finish = MPE_Log_get_event_number( );
+ ev_global_stat_start = MPE_Log_get_event_number( );
+ ev_global_stat_finish = MPE_Log_get_event_number( );
+ ev_virial_start = MPE_Log_get_event_number( );
+ ev_virial_finish = MPE_Log_get_event_number( );
+
+ /* Enforced rotation */
+ ev_flexll_start = MPE_Log_get_event_number( );
+ ev_flexll_finish = MPE_Log_get_event_number( );
+ ev_add_rot_forces_start = MPE_Log_get_event_number( );
+ ev_add_rot_forces_finish = MPE_Log_get_event_number( );
+ ev_rotcycles_start = MPE_Log_get_event_number( );
+ ev_rotcycles_finish = MPE_Log_get_event_number( );
+ ev_forcecycles_start = MPE_Log_get_event_number( );
+ ev_forcecycles_finish = MPE_Log_get_event_number( );
+
+ /* Shift related events */
+ ev_shift_start = MPE_Log_get_event_number( );
+ ev_shift_finish = MPE_Log_get_event_number( );
+ ev_unshift_start = MPE_Log_get_event_number( );
+ ev_unshift_finish = MPE_Log_get_event_number( );
+ ev_mk_mshift_start = MPE_Log_get_event_number( );
+ ev_mk_mshift_finish = MPE_Log_get_event_number( );
+
+ /* PME related events */
+ ev_pme_start = MPE_Log_get_event_number( );
+ ev_pme_finish = MPE_Log_get_event_number( );
+ ev_spread_on_grid_start = MPE_Log_get_event_number( );
+ ev_spread_on_grid_finish = MPE_Log_get_event_number( );
+ ev_sum_qgrid_start = MPE_Log_get_event_number( );
+ ev_sum_qgrid_finish = MPE_Log_get_event_number( );
+ ev_gmxfft3d_start = MPE_Log_get_event_number( );
+ ev_gmxfft3d_finish = MPE_Log_get_event_number( );
+ ev_solve_pme_start = MPE_Log_get_event_number( );
+ ev_solve_pme_finish = MPE_Log_get_event_number( );
+ ev_gather_f_bsplines_start = MPE_Log_get_event_number( );
+ ev_gather_f_bsplines_finish= MPE_Log_get_event_number( );
+ ev_reduce_start = MPE_Log_get_event_number( );
+ ev_reduce_finish = MPE_Log_get_event_number( );
+ ev_rscatter_start = MPE_Log_get_event_number( );
+ ev_rscatter_finish = MPE_Log_get_event_number( );
+ ev_alltoall_start = MPE_Log_get_event_number( );
+ ev_alltoall_finish = MPE_Log_get_event_number( );
+ ev_pmeredist_start = MPE_Log_get_event_number( );
+ ev_pmeredist_finish = MPE_Log_get_event_number( );
+ ev_init_pme_start = MPE_Log_get_event_number( );
+ ev_init_pme_finish = MPE_Log_get_event_number( );
+ ev_send_coordinates_start = MPE_Log_get_event_number( );
+ ev_send_coordinates_finish = MPE_Log_get_event_number( );
+ ev_update_fr_start = MPE_Log_get_event_number( );
+ ev_update_fr_finish = MPE_Log_get_event_number( );
+ ev_clear_rvecs_start = MPE_Log_get_event_number( );
+ ev_clear_rvecs_finish = MPE_Log_get_event_number( );
+ ev_update_start = MPE_Log_get_event_number( );
+ ev_update_finish = MPE_Log_get_event_number( );
+ ev_output_start = MPE_Log_get_event_number( );
+ ev_output_finish = MPE_Log_get_event_number( );
+ ev_sum_lrforces_start = MPE_Log_get_event_number( );
+ ev_sum_lrforces_finish = MPE_Log_get_event_number( );
+ ev_sort_start = MPE_Log_get_event_number( );
+ ev_sort_finish = MPE_Log_get_event_number( );
+ ev_sum_qgrid_start = MPE_Log_get_event_number( );
+ ev_sum_qgrid_finish = MPE_Log_get_event_number( );
+
+ /* Essential dynamics related events */
+ ev_edsam_start = MPE_Log_get_event_number( );
+ ev_edsam_finish = MPE_Log_get_event_number( );
+ ev_get_coords_start = MPE_Log_get_event_number( );
+ ev_get_coords_finish = MPE_Log_get_event_number( );
+ ev_ed_apply_cons_start = MPE_Log_get_event_number( );
+ ev_ed_apply_cons_finish = MPE_Log_get_event_number( );
+ ev_fit_to_reference_start = MPE_Log_get_event_number( );
+ ev_fit_to_reference_finish = MPE_Log_get_event_number( );
+
+ /* describe events: */
+ if ( mpi_my_rank == 0 )
+ {
+ /* General events */
+ MPE_Describe_state(ev_timestep1, ev_timestep2, "timestep START", "magenta" );
+ MPE_Describe_state(ev_force_start, ev_force_finish, "force", "cornflower blue" );
+ MPE_Describe_state(ev_do_fnbf_start, ev_do_fnbf_finish, "do_fnbf", "navy" );
+ MPE_Describe_state(ev_ns_start, ev_ns_finish, "neighbor search", "tomato" );
+ MPE_Describe_state(ev_calc_bonds_start, ev_calc_bonds_finish, "bonded forces", "slate blue" );
+ MPE_Describe_state(ev_global_stat_start, ev_global_stat_finish, "global stat", "firebrick3");
+ MPE_Describe_state(ev_update_fr_start, ev_update_fr_finish, "update forcerec", "goldenrod");
+ MPE_Describe_state(ev_clear_rvecs_start, ev_clear_rvecs_finish, "clear rvecs", "bisque");
+ MPE_Describe_state(ev_update_start, ev_update_finish, "update", "cornsilk");
+ MPE_Describe_state(ev_output_start, ev_output_finish, "output", "black");
+ MPE_Describe_state(ev_virial_start, ev_virial_finish, "calc_virial", "thistle4");
+
+ /* Enforced rotation */
+ MPE_Describe_state(ev_flexll_start, ev_flexll_finish, "flex lowlevel", "navajo white");
+ MPE_Describe_state(ev_add_rot_forces_start, ev_add_rot_forces_finish, "add rot forces", "green");
+ MPE_Describe_state(ev_rotcycles_start, ev_rotcycles_finish, "count rot cyc", "moccasin");
+ MPE_Describe_state(ev_forcecycles_start, ev_forcecycles_finish, "count force cyc", "powder blue");
+
+ /* PME related events */
+ MPE_Describe_state(ev_pme_start, ev_pme_finish, "doing PME", "grey" );
+ MPE_Describe_state(ev_spread_on_grid_start, ev_spread_on_grid_finish, "spread", "dark orange" );
+ MPE_Describe_state(ev_sum_qgrid_start, ev_sum_qgrid_finish, "sum qgrid", "slate blue");
+ MPE_Describe_state(ev_gmxfft3d_start, ev_gmxfft3d_finish, "fft3d", "snow2" );
+ MPE_Describe_state(ev_solve_pme_start, ev_solve_pme_finish, "solve PME", "indian red" );
+ MPE_Describe_state(ev_gather_f_bsplines_start, ev_gather_f_bsplines_finish, "bsplines", "light sea green" );
+ MPE_Describe_state(ev_reduce_start, ev_reduce_finish, "reduce", "cyan1" );
+ MPE_Describe_state(ev_rscatter_start, ev_rscatter_finish, "rscatter", "cyan3" );
+ MPE_Describe_state(ev_alltoall_start, ev_alltoall_finish, "alltoall", "LightCyan4" );
+ MPE_Describe_state(ev_pmeredist_start, ev_pmeredist_finish, "pmeredist", "thistle" );
+ MPE_Describe_state(ev_init_pme_start, ev_init_pme_finish, "init PME", "snow4");
+ MPE_Describe_state(ev_send_coordinates_start, ev_send_coordinates_finish, "send_coordinates","blue");
+ MPE_Describe_state(ev_sum_lrforces_start, ev_sum_lrforces_finish, "sum_LRforces", "lime green");
+ MPE_Describe_state(ev_sort_start, ev_sort_finish, "sort pme atoms", "brown");
+ MPE_Describe_state(ev_sum_qgrid_start, ev_sum_qgrid_finish, "sum charge grid", "medium orchid");
+
+ /* Shift related events */
+ MPE_Describe_state(ev_shift_start, ev_shift_finish, "shift", "orange");
+ MPE_Describe_state(ev_unshift_start, ev_unshift_finish, "unshift", "dark orange");
+ MPE_Describe_state(ev_mk_mshift_start, ev_mk_mshift_finish, "mk_mshift", "maroon");
+
+ /* Essential dynamics related events */
+ MPE_Describe_state(ev_edsam_start, ev_edsam_finish, "EDSAM", "deep sky blue");
+ MPE_Describe_state(ev_get_coords_start, ev_get_coords_finish, "ED get coords", "steel blue");
+ MPE_Describe_state(ev_ed_apply_cons_start, ev_ed_apply_cons_finish, "ED apply constr", "forest green");
+ MPE_Describe_state(ev_fit_to_reference_start, ev_fit_to_reference_finish, "ED fit to ref", "lavender");
+
+ }
+ MPE_Init_log();
+#endif
+
+#ifdef GMX_LIB_MPI
+ fprintf(stderr,"NNODES=%d, MYRANK=%d, HOSTNAME=%s\n",
+ mpi_num_nodes,mpi_my_rank,mpi_hostname);
+#endif
+
+ *nnodes=mpi_num_nodes;
+
+ return mpi_my_rank;
+#endif
+}
+
+int gmx_node_num(void)
+{
+#ifndef GMX_MPI
+ return 1;
+#else
+ int i;
+ (void) MPI_Comm_size(MPI_COMM_WORLD, &i);
+ return i;
+#endif
+}
+
+int gmx_node_rank(void)
+{
+#ifndef GMX_MPI
+ return 0;
+#else
+ int i;
+ (void) MPI_Comm_rank(MPI_COMM_WORLD, &i);
+ return i;
+#endif
+}
+
+void gmx_setup_nodecomm(FILE *fplog,t_commrec *cr)
+{
+ gmx_nodecomm_t *nc;
+ int n,rank,resultlen,hostnum,i,j,ng,ni;
+#ifdef GMX_MPI
+ char mpi_hostname[MPI_MAX_PROCESSOR_NAME],num[MPI_MAX_PROCESSOR_NAME];
+#endif
+
+ /* Many MPI implementations do not optimize MPI_Allreduce
+ * (and probably also other global communication calls)
+ * for multi-core nodes connected by a network.
+ * We can optimize such communication by using one MPI call
+ * within each node and one between the nodes.
+ * For MVAPICH2 and Intel MPI this reduces the time for
+ * the global_stat communication by 25%
+ * for 2x2-core 3 GHz Woodcrest connected by mixed DDR/SDR Infiniband.
+ * B. Hess, November 2007
+ */
+
+ nc = &cr->nc;
+
+ nc->bUse = FALSE;
+#ifndef GMX_THREADS
+ if (getenv("GMX_NO_NODECOMM") == NULL) {
+#ifdef GMX_MPI
+ MPI_Comm_size(cr->mpi_comm_mygroup,&n);
+ MPI_Comm_rank(cr->mpi_comm_mygroup,&rank);
+ MPI_Get_processor_name(mpi_hostname,&resultlen);
+ /* This procedure can only differentiate nodes with host names
+ * that end on unique numbers.
+ */
+ i = 0;
+ j = 0;
+ /* Only parse the host name up to the first dot */
+ while(i < resultlen && mpi_hostname[i] != '.') {
+ if (isdigit(mpi_hostname[i])) {
+ num[j++] = mpi_hostname[i];
+ }
+ i++;
+ }
+ num[j] = '\0';
+ if (j == 0) {
+ hostnum = 0;
+ } else {
+ /* Use only the last 9 decimals, so we don't overflow an int */
+ hostnum = strtol(num + max(0,j-9), NULL, 10);
+ }
+
+ if (debug) {
+ fprintf(debug,
+ "In gmx_setup_nodecomm: splitting communicator of size %d\n",
+ n);
+ fprintf(debug,"In gmx_setup_nodecomm: hostname '%s', hostnum %d\n",
+ mpi_hostname,hostnum);
+ }
+
+ /* The intra-node communicator, split on node number */
+ MPI_Comm_split(cr->mpi_comm_mygroup,hostnum,rank,&nc->comm_intra);
+ MPI_Comm_rank(nc->comm_intra,&nc->rank_intra);
+ if (debug) {
+ fprintf(debug,"In gmx_setup_nodecomm: node rank %d rank_intra %d\n",
+ rank,nc->rank_intra);
+ }
+ /* The inter-node communicator, split on rank_intra.
+ * We actually only need the one for rank=0,
+ * but it is easier to create them all.
+ */
+ MPI_Comm_split(cr->mpi_comm_mygroup,nc->rank_intra,rank,&nc->comm_inter);
+ /* Check if this really created two step communication */
+ MPI_Comm_size(nc->comm_inter,&ng);
+ MPI_Comm_size(nc->comm_intra,&ni);
+ if (debug) {
+ fprintf(debug,"In gmx_setup_nodecomm: groups %d, my group size %d\n",
+ ng,ni);
+ }
+ if ((ng > 1 && ng < n) || (ni > 1 && ni < n)) {
+ nc->bUse = TRUE;
+ if (fplog)
+ fprintf(fplog,"Using two step summing over %d groups of on average %.1f processes\n\n",ng,(real)n/(real)ng);
+ if (nc->rank_intra > 0)
+ MPI_Comm_free(&nc->comm_inter);
+ } else {
+ /* One group or all processes in a separate group, use normal summing */
+ MPI_Comm_free(&nc->comm_inter);
+ MPI_Comm_free(&nc->comm_intra);
+ }
+#endif
+ }
+#endif
+}
+
+void gmx_barrier(const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_barrier");
+#else
+ MPI_Barrier(cr->mpi_comm_mygroup);
+#endif
+}
+
+void gmx_abort(int noderank,int nnodes,int errorno)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_abort");
+#else
+#ifdef GMX_THREADS
+ fprintf(stderr,"Halting program %s\n",ShortProgram());
+ thanx(stderr);
+ exit(1);
+#else
+ if (nnodes > 1)
+ {
+ fprintf(stderr,"Halting parallel program %s on CPU %d out of %d\n",
+ ShortProgram(),noderank,nnodes);
+ }
+ else
+ {
+ fprintf(stderr,"Halting program %s\n",ShortProgram());
+ }
+
+ thanx(stderr);
+ MPI_Abort(MPI_COMM_WORLD,errorno);
+ exit(1);
+#endif
+#endif
+}
+
+void gmx_bcast(int nbytes,void *b,const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_bast");
+#else
+ MPI_Bcast(b,nbytes,MPI_BYTE,MASTERRANK(cr),cr->mpi_comm_mygroup);
+#endif
+}
+
+void gmx_bcast_sim(int nbytes,void *b,const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_bast");
+#else
+ MPI_Bcast(b,nbytes,MPI_BYTE,MASTERRANK(cr),cr->mpi_comm_mysim);
+#endif
+}
+
+void gmx_sumd(int nr,double r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumd");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
+ if (cr->nc.bUse) {
+ if (cr->nc.rank_intra == 0)
+ {
+ /* Use two step summing. */
+ MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,0,
+ cr->nc.comm_intra);
+ /* Sum the roots of the internal (intra) buffers. */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,MPI_DOUBLE,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,MPI_DOUBLE,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->dbuf_alloc) {
+ cr->mpb->dbuf_alloc = nr;
+ srenew(cr->mpb->dbuf,cr->mpb->dbuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->dbuf,nr,MPI_DOUBLE,MPI_SUM,cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->dbuf,r,nr,MPI_DOUBLE,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,MPI_DOUBLE,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->dbuf,nr,MPI_DOUBLE,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->dbuf[i];
+ }
+#endif
+#endif
+}
+
+void gmx_sumf(int nr,float r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumf");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
+ if (cr->nc.bUse) {
+ /* Use two step summing. */
+ if (cr->nc.rank_intra == 0)
+ {
+ MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,0,
+ cr->nc.comm_intra);
+ /* Sum the roots of the internal (intra) buffers */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,MPI_FLOAT,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,MPI_FLOAT,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->fbuf_alloc) {
+ cr->mpb->fbuf_alloc = nr;
+ srenew(cr->mpb->fbuf,cr->mpb->fbuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->fbuf,nr,MPI_FLOAT,MPI_SUM,cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->fbuf,r,nr,MPI_FLOAT,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,MPI_FLOAT,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->fbuf,nr,MPI_FLOAT,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->fbuf[i];
+ }
+#endif
+#endif
+}
+
+void gmx_sumi(int nr,int r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumi");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ if (cr->nc.rank_intra == 0)
+ {
+ MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,0,cr->nc.comm_intra);
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,MPI_INT,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,MPI_INT,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->ibuf_alloc) {
+ cr->mpb->ibuf_alloc = nr;
+ srenew(cr->mpb->ibuf,cr->mpb->ibuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->ibuf,nr,MPI_INT,MPI_SUM,cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->ibuf,r,nr,MPI_INT,MPI_SUM,cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,MPI_INT,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->ibuf,nr,MPI_INT,MPI_SUM,cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->ibuf[i];
+ }
+#endif
+#endif
+}
+
+#ifdef GMX_MPI
+void gmx_sumd_comm(int nr,double r[],MPI_Comm mpi_comm)
+{
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,mpi_comm);
+#else
+ /* this function is only used in code that is not performance critical,
+ (during setup, when comm_rec is not the appropriate communication
+ structure), so this isn't as bad as it looks. */
+ double *buf;
+ int i;
+
+ snew(buf, nr);
+ MPI_Allreduce(r,buf,nr,MPI_DOUBLE,MPI_SUM,mpi_comm);
+ for(i=0; i<nr; i++)
+ r[i] = buf[i];
+ sfree(buf);
+#endif
+}
+#endif
+
+#ifdef GMX_MPI
+void gmx_sumf_comm(int nr,float r[],MPI_Comm mpi_comm)
+{
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,mpi_comm);
+#else
+ /* this function is only used in code that is not performance critical,
+ (during setup, when comm_rec is not the appropriate communication
+ structure), so this isn't as bad as it looks. */
+ float *buf;
+ int i;
+
+ snew(buf, nr);
+ MPI_Allreduce(r,buf,nr,MPI_FLOAT,MPI_SUM,mpi_comm);
+ for(i=0; i<nr; i++)
+ r[i] = buf[i];
+ sfree(buf);
+#endif
+}
+#endif
+
+void gmx_sumd_sim(int nr,double r[],const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumd");
+#else
+ gmx_sumd_comm(nr,r,ms->mpi_comm_masters);
+#endif
+}
+
+void gmx_sumf_sim(int nr,float r[],const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumf");
+#else
+ gmx_sumf_comm(nr,r,ms->mpi_comm_masters);
+#endif
+}
+
+void gmx_sumi_sim(int nr,int r[], const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumd");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREADS)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,ms->mpi_comm_masters);
+#else
+ /* this is thread-unsafe, but it will do for now: */
+ int i;
+
+ if (nr > ms->mpb->ibuf_alloc) {
+ ms->mpb->ibuf_alloc = nr;
+ srenew(ms->mpb->ibuf,ms->mpb->ibuf_alloc);
+ }
+ MPI_Allreduce(r,ms->mpb->ibuf,nr,MPI_INT,MPI_SUM,ms->mpi_comm_masters);
+ for(i=0; i<nr; i++)
+ r[i] = ms->mpb->ibuf[i];
+#endif
+#endif
+}
+
+void gmx_finalize(void)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_finalize");
+#else
+ int ret;
+
+ /* just as a check; we don't want to finalize twice */
+ int finalized;
+ MPI_Finalized(&finalized);
+ if (finalized)
+ return;
+
+ /* We sync the processes here to try to avoid problems
+ * with buggy MPI implementations that could cause
+ * unfinished processes to terminate.
+ */
+ MPI_Barrier(MPI_COMM_WORLD);
+
+ /*
+ if (DOMAINDECOMP(cr)) {
+ if (cr->npmenodes > 0 || cr->dd->bCartesian)
+ MPI_Comm_free(&cr->mpi_comm_mygroup);
+ if (cr->dd->bCartesian)
+ MPI_Comm_free(&cr->mpi_comm_mysim);
+ }
+ */
+
+ /* Apparently certain mpich implementations cause problems
+ * with MPI_Finalize. In that case comment out MPI_Finalize.
+ */
+ if (debug)
+ fprintf(debug,"Will call MPI_Finalize now\n");
+
+ ret = MPI_Finalize();
+ if (debug)
+ fprintf(debug,"Return code from MPI_Finalize = %d\n",ret);
+#endif
+}
+
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ * $Id: molprop_util.c,v 1.51 2009/06/01 06:13:18 spoel Exp $
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 4.0.99
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Groningen Machine for Chemical Simulation
+ */
+#include <stdio.h>
+#include "string2.h"
+#include "physics.h"
+
+double convert2gmx(double x,int unit)
+{
+ switch (unit)
+ {
+ case eg2cAngstrom:
+ return x*A2NM;
+ case eg2cNm:
+ return x;
+ case eg2cBohr:
+ return x*BOHR2NM;
+ case eg2cKcal_Mole:
+ return x/CAL2JOULE;
+ case eg2cHartree:
+ return x*ONE_4PI_EPS0/BOHR2NM;
+ case eg2cHartree_e:
+ return x*ONE_4PI_EPS0/BOHR2NM;
+ case eg2cAngstrom3:
+ return x*A2NM*A2NM*A2NM;
+ case eg2cCoulomb:
+ return x/E_CHARGE;
+ case eg2cDebye:
+ return x*DEBYE2ENM;
+ case eg2cElectron:
+ return x;
+ case eg2cBuckingham:
+ return x*A2NM*DEBYE2ENM;
+ default:
+ fprintf(stderr,"Unknown unit %d, not converting.\n",unit);
+ }
+ return x;
+}
+
+double gmx2convert(double x,int unit)
+{
+ switch (unit)
+ {
+ case eg2cAngstrom:
+ return x/A2NM;
+ case eg2cNm:
+ return x;
+ case eg2cBohr:
+ return x/BOHR2NM;
+ case eg2cKcal_Mole:
+ return x*CAL2JOULE;
+ case eg2cHartree:
+ return x/(ONE_4PI_EPS0/BOHR2NM);
+ case eg2cHartree_e:
+ return x/(ONE_4PI_EPS0/BOHR2NM);
+ case eg2cAngstrom3:
+ return x/(A2NM*A2NM*A2NM);
+ case eg2cCoulomb:
+ return x*E_CHARGE;
+ case eg2cDebye:
+ return x/DEBYE2ENM;
+ case eg2cElectron:
+ return x;
+ case eg2cBuckingham:
+ return x/(A2NM*DEBYE2ENM);
+ default:
+ fprintf(stderr,"Unknown unit %d, not converting.\n",unit);
+ }
+ return x;
+}
+
+/* This has to have the same order as the enums. */
+static const char *eg2c_names[eg2cNR] = {
+ "Angstrom", "Nm", "Bohr", "Kcal_Mole",
+ "Hartree", "Hartree_e", "Angstrom3", "Coulomb",
+ "Debye", "Electron", "Buckingham"
+};
+
+int string2unit(char *string)
+{
+ int i;
+
+ for(i=0; (i<eg2cNR); i++)
+ if (strcasecmp(string,eg2c_names[i]) == 0)
+ return i;
+ return -1;
+}
+
+const char *unit2string(int unit)
+{
+ if ((unit >= 0) && (unit < eg2cNR))
+ return eg2c_names[unit];
+
+ return NULL;
+}
--- /dev/null
+#include <stdio.h>
+#include "physics.h"
+
+int main(int argc,char *argv[])
+{
+ int i;
+ double x,y,z;
+
+ x = 3.25;
+ for(i=0; (i<eg2cNR); i++) {
+ y = gmx2convert(x,i);
+ z = convert2gmx(y,i);
+ printf("Converted %g [type %d] to %g and back to %g. Diff %g\n",
+ x,i,y,z,x-z);
+ }
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*- */
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Green Red Orange Magenta Azure Cyan Skyblue
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include "typedefs.h"
+#include "smalloc.h"
+#include "gmx_statistics.h"
+
+static double sqr(double x)
+{
+ return x*x;
+}
+
+static int gmx_nint(double x)
+{
+ return (int) (x+0.5);
+}
+
+typedef struct gmx_stats {
+ double aa,a,b,sigma_aa,sigma_a,sigma_b,aver,sigma_aver,error;
+ double rmsd,Rdata,Rfit,Rfitaa,chi2,chi2aa;
+ double *x,*y,*dx,*dy;
+ int computed;
+ int np,np_c,nalloc;
+} gmx_stats;
+
+gmx_stats_t gmx_stats_init()
+{
+ gmx_stats *stats;
+
+ snew(stats,1);
+
+ return (gmx_stats_t) stats;
+}
+
+int gmx_stats_get_npoints(gmx_stats_t gstats, int *N)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+
+ *N = stats->np;
+
+ return estatsOK;
+}
+
+int gmx_stats_done(gmx_stats_t gstats)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+
+ sfree(stats->x);
+ stats->x = NULL;
+ sfree(stats->y);
+ stats->y = NULL;
+ sfree(stats->dx);
+ stats->dx = NULL;
+ sfree(stats->dy);
+ stats->dy = NULL;
+
+ return estatsOK;
+}
+
+int gmx_stats_add_point(gmx_stats_t gstats,double x,double y,
+ double dx,double dy)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i;
+
+ if (stats->np+1 >= stats->nalloc)
+ {
+ if (stats->nalloc == 0)
+ stats->nalloc = 1024;
+ else
+ stats->nalloc *= 2;
+ srenew(stats->x,stats->nalloc);
+ srenew(stats->y,stats->nalloc);
+ srenew(stats->dx,stats->nalloc);
+ srenew(stats->dy,stats->nalloc);
+ for(i=stats->np; (i<stats->nalloc); i++)
+ {
+ stats->x[i] = 0;
+ stats->y[i] = 0;
+ stats->dx[i] = 0;
+ stats->dy[i] = 0;
+ }
+ }
+ stats->x[stats->np] = x;
+ stats->y[stats->np] = y;
+ stats->dx[stats->np] = dx;
+ stats->dy[stats->np] = dy;
+ stats->np++;
+ stats->computed = 0;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_point(gmx_stats_t gstats,real *x,real *y,
+ real *dx,real *dy,real level)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok,outlier;
+ real rmsd,r;
+
+ if ((ok = gmx_stats_get_rmsd(gstats,&rmsd)) != estatsOK)
+ {
+ return ok;
+ }
+ outlier = 0;
+ while ((outlier == 0) && (stats->np_c < stats->np))
+ {
+ r = fabs(stats->x[stats->np_c] - stats->y[stats->np_c]);
+ outlier = (r > rmsd*level);
+ if (outlier)
+ {
+ if (NULL != x) *x = stats->x[stats->np_c];
+ if (NULL != y) *y = stats->y[stats->np_c];
+ if (NULL != dx) *dx = stats->dx[stats->np_c];
+ if (NULL != dy) *dy = stats->dy[stats->np_c];
+ }
+ stats->np_c++;
+
+ if (outlier)
+ return estatsOK;
+ }
+
+ stats->np_c = 0;
+
+ return estatsNO_POINTS;
+}
+
+int gmx_stats_add_points(gmx_stats_t gstats,int n,real *x,real *y,
+ real *dx,real *dy)
+{
+ int i,ok;
+
+ for(i=0; (i<n); i++)
+ {
+ if ((ok = gmx_stats_add_point(gstats,x[i],y[i],
+ (NULL != dx) ? dx[i] : 0,
+ (NULL != dy) ? dy[i] : 0)) != estatsOK)
+ {
+ return ok;
+ }
+ }
+ return estatsOK;
+}
+
+static int gmx_stats_compute(gmx_stats *stats,int weight)
+{
+ double yy,yx,xx,sx,sy,dy,chi2,chi2aa,d2;
+ double ssxx,ssyy,ssxy;
+ double w,wtot,yx_nw,sy_nw,sx_nw,yy_nw,xx_nw,dx2,dy2;
+ int i,N;
+
+ N = stats->np;
+ if (stats->computed == 0)
+ {
+ if (N < 1)
+ {
+ return estatsNO_POINTS;
+ }
+
+ xx = xx_nw = 0;
+ yy = yy_nw = 0;
+ yx = yx_nw = 0;
+ sx = sx_nw = 0;
+ sy = sy_nw = 0;
+ wtot = 0;
+ d2 = 0;
+ for(i=0; (i<N); i++)
+ {
+ d2 += sqr(stats->x[i]-stats->y[i]);
+ if ((stats->dy[i]) && (weight == elsqWEIGHT_Y))
+ {
+ w = 1/sqr(stats->dy[i]);
+ }
+ else
+ {
+ w = 1;
+ }
+
+ wtot += w;
+
+ xx += w*sqr(stats->x[i]);
+ xx_nw += sqr(stats->x[i]);
+
+ yy += w*sqr(stats->y[i]);
+ yy_nw += sqr(stats->y[i]);
+
+ yx += w*stats->y[i]*stats->x[i];
+ yx_nw += stats->y[i]*stats->x[i];
+
+ sx += w*stats->x[i];
+ sx_nw += stats->x[i];
+
+ sy += w*stats->y[i];
+ sy_nw += stats->y[i];
+ }
+
+ /* Compute average, sigma and error */
+ stats->aver = sy_nw/N;
+ stats->sigma_aver = sqrt(yy_nw/N - sqr(sy_nw/N));
+ stats->error = stats->sigma_aver/sqrt(N);
+
+ /* Compute RMSD between x and y */
+ stats->rmsd = sqrt(d2/N);
+
+ /* Correlation coefficient for data */
+ yx_nw /= N;
+ xx_nw /= N;
+ yy_nw /= N;
+ sx_nw /= N;
+ sy_nw /= N;
+ ssxx = N*(xx_nw - sqr(sx_nw));
+ ssyy = N*(yy_nw - sqr(sy_nw));
+ ssxy = N*(yx_nw - (sx_nw*sy_nw));
+ stats->Rdata = sqrt(sqr(ssxy)/(ssxx*ssyy));
+
+ /* Compute straight line through datapoints, either with intercept
+ zero (result in aa) or with intercept variable (results in a
+ and b) */
+ yx = yx/wtot;
+ xx = xx/wtot;
+ sx = sx/wtot;
+ sy = sy/wtot;
+
+ stats->aa = (yx/xx);
+ stats->a = (yx-sx*sy)/(xx-sx*sx);
+ stats->b = (sy)-(stats->a)*(sx);
+
+ /* Compute chi2, deviation from a line y = ax+b. Also compute
+ chi2aa which returns the deviation from a line y = ax. */
+ chi2 = 0;
+ chi2aa = 0;
+ for(i=0; (i<N); i++)
+ {
+ if (stats->dy[i] > 0)
+ {
+ dy = stats->dy[i];
+ }
+ else
+ {
+ dy = 1;
+ }
+ chi2aa += sqr((stats->y[i]-(stats->aa*stats->x[i]))/dy);
+ chi2 += sqr((stats->y[i]-(stats->a*stats->x[i]+stats->b))/dy);
+ }
+ if (N > 2)
+ {
+ stats->chi2 = sqrt(chi2/(N-2));
+ stats->chi2aa = sqrt(chi2aa/(N-2));
+
+ /* Look up equations! */
+ dx2 = (xx-sx*sx);
+ dy2 = (yy-sy*sy);
+ stats->sigma_a = sqrt(stats->chi2/((N-2)*dx2));
+ stats->sigma_b = stats->sigma_a*sqrt(xx);
+ stats->Rfit = fabs(ssxy)/sqrt(ssxx*ssyy);
+ /*stats->a*sqrt(dx2/dy2);*/
+ stats->Rfitaa = stats->aa*sqrt(dx2/dy2);
+ }
+ else
+ {
+ stats->chi2 = 0;
+ stats->chi2aa = 0;
+ stats->sigma_a = 0;
+ stats->sigma_b = 0;
+ stats->Rfit = 0;
+ stats->Rfitaa = 0;
+ }
+
+ stats->computed = 1;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_get_ab(gmx_stats_t gstats,int weight,
+ real *a,real *b,real *da,real *db,
+ real *chi2,real *Rfit)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,weight)) != estatsOK)
+ return ok;
+ if (NULL != a)
+ {
+ *a = stats->a;
+ }
+ if (NULL != b)
+ {
+ *b = stats->b;
+ }
+ if (NULL != da)
+ {
+ *da = stats->sigma_a;
+ }
+ if (NULL != db)
+ {
+ *db = stats->sigma_b;
+ }
+ if (NULL != chi2)
+ {
+ *chi2 = stats->chi2;
+ }
+ if (NULL != Rfit)
+ {
+ *Rfit = stats->Rfit;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_get_a(gmx_stats_t gstats,int weight,real *a,real *da,
+ real *chi2,real *Rfit)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,weight)) != estatsOK)
+ return ok;
+ if (NULL != a) *a = stats->aa;
+ if (NULL != da) *da = stats->sigma_aa;
+ if (NULL != chi2) *chi2 = stats->chi2aa;
+ if (NULL != Rfit) *Rfit = stats->Rfitaa;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_average(gmx_stats_t gstats,real *aver)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ {
+ return ok;
+ }
+
+ *aver = stats->aver;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_ase(gmx_stats_t gstats,real *aver,real *sigma,real *error)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ {
+ return ok;
+ }
+
+ if (NULL != aver)
+ {
+ *aver = stats->aver;
+ }
+ if (NULL != sigma)
+ {
+ *sigma = stats->sigma_aver;
+ }
+ if (NULL != error)
+ {
+ *error = stats->error;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_get_sigma(gmx_stats_t gstats,real *sigma)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ return ok;
+
+ *sigma = stats->sigma_aver;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_error(gmx_stats_t gstats,real *error)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ return ok;
+
+ *error = stats->error;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_corr_coeff(gmx_stats_t gstats,real *R)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ return ok;
+
+ *R = stats->Rdata;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_rmsd(gmx_stats_t gstats,real *rmsd)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ {
+ return ok;
+ }
+
+ *rmsd = stats->rmsd;
+
+ return estatsOK;
+}
+
+int gmx_stats_dump_xy(gmx_stats_t gstats,FILE *fp)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i,ok;
+
+ for(i=0; (i<stats->np); i++)
+ {
+ fprintf(fp,"%12g %12g %12g %12g\n",stats->x[i],stats->y[i],
+ stats->dx[i],stats->dy[i]);
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_remove_outliers(gmx_stats_t gstats,double level)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i,iter=1,done=0,ok;
+ real rmsd,r;
+
+ while ((stats->np >= 10) && !done)
+ {
+ if ((ok = gmx_stats_get_rmsd(gstats,&rmsd)) != estatsOK)
+ {
+ return ok;
+ }
+ done = 1;
+ for(i=0; (i<stats->np); )
+ {
+ r = fabs(stats->x[i]-stats->y[i]);
+ if (r > level*rmsd)
+ {
+ fprintf(stderr,"Removing outlier, iter = %d, rmsd = %g, x = %g, y = %g\n",
+ iter,rmsd,stats->x[i],stats->y[i]);
+ if (i < stats->np-1)
+ {
+ stats->x[i] = stats->x[stats->np-1];
+ stats->y[i] = stats->y[stats->np-1];
+ stats->dx[i] = stats->dx[stats->np-1];
+ stats->dy[i] = stats->dy[stats->np-1];
+ }
+ stats->np--;
+ done = 0;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ iter++;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_make_histogram(gmx_stats_t gstats,real binwidth,int *nb,
+ int ehisto,int normalized,real **x,real **y)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i,ok,index=0,nbins=*nb,*nindex;
+ double minx,maxx,maxy,miny,delta,dd,minh;
+
+ if (((binwidth <= 0) && (nbins <= 0)) ||
+ ((binwidth > 0) && (nbins > 0)))
+ {
+ return estatsINVALID_INPUT;
+ }
+ if (stats->np <= 2)
+ {
+ return estatsNO_POINTS;
+ }
+ minx = maxx = stats->x[0];
+ miny = maxy = stats->y[0];
+ for(i=1; (i<stats->np); i++)
+ {
+ miny = (stats->y[i] < miny) ? stats->y[i] : miny;
+ maxy = (stats->y[i] > maxy) ? stats->y[i] : maxy;
+ minx = (stats->x[i] < minx) ? stats->x[i] : minx;
+ maxx = (stats->x[i] > maxx) ? stats->x[i] : maxx;
+ }
+ if (ehisto == ehistoX)
+ {
+ delta = maxx-minx;
+ minh = minx;
+ }
+ else if (ehisto == ehistoY)
+ {
+ delta = maxy-miny;
+ minh = miny;
+ }
+ else
+ return estatsINVALID_INPUT;
+
+ if (binwidth == 0)
+ {
+ binwidth = (delta)/nbins;
+ }
+ else
+ {
+ nbins = gmx_nint((delta)/binwidth + 0.5);
+ }
+ snew(*x,nbins);
+ snew(nindex,nbins);
+ for(i=0; (i<nbins); i++)
+ {
+ (*x)[i] = minh + binwidth*(i+0.5);
+ }
+ if (normalized == 0)
+ {
+ dd = 1;
+ }
+ else
+ {
+ dd = 1.0/(binwidth*stats->np);
+ }
+
+ snew(*y,nbins);
+ for(i=0; (i<stats->np); i++)
+ {
+ if (ehisto == ehistoY)
+ index = (stats->y[i]-miny)/binwidth;
+ else if (ehisto == ehistoX)
+ index = (stats->x[i]-minx)/binwidth;
+ if (index<0)
+ {
+ index = 0;
+ }
+ if (index>nbins-1)
+ {
+ index = nbins-1;
+ }
+ (*y)[index] += dd;
+ nindex[index]++;
+ }
+ if (*nb == 0)
+ *nb = nbins;
+ for(i=0; (i<nbins); i++)
+ if (nindex[i] > 0)
+ (*y)[i] /= nindex[i];
+
+ sfree(nindex);
+
+ return estatsOK;
+}
+
+static const char *stats_error[estatsNR] =
+{
+ "All well in STATS land",
+ "No points",
+ "Not enough memory",
+ "Invalid histogram input",
+ "Unknown error",
+ "Not implemented yet"
+};
+
+const char *gmx_stats_message(int estats)
+{
+ if ((estats >= 0) && (estats < estatsNR))
+ {
+ return stats_error[estats];
+ }
+ else
+ {
+ return stats_error[estatsERROR];
+ }
+}
+
+/* Old convenience functions, should be merged with the core
+ statistics above. */
+int lsq_y_ax(int n, real x[], real y[], real *a)
+{
+ gmx_stats_t lsq = gmx_stats_init();
+ int ok;
+ real da,chi2,Rfit;
+
+ gmx_stats_add_points(lsq,n,x,y,0,0);
+ if ((ok = gmx_stats_get_a(lsq,elsqWEIGHT_NONE,a,&da,&chi2,&Rfit)) != estatsOK)
+ {
+ return ok;
+ }
+
+ /* int i;
+ double xx,yx;
+
+ yx=xx=0.0;
+ for (i=0; i<n; i++) {
+ yx+=y[i]*x[i];
+ xx+=x[i]*x[i];
+ }
+ *a=yx/xx;
+ */
+ return estatsOK;
+}
+
+static int low_lsq_y_ax_b(int n, real *xr, double *xd, real yr[],
+ real *a, real *b,real *r,real *chi2)
+{
+ int i,ok;
+ gmx_stats_t lsq;
+
+ lsq = gmx_stats_init();
+ for(i=0; (i<n); i++)
+ {
+ if ((ok = gmx_stats_add_point(lsq,(NULL != xd) ? xd[i] : xr[i],yr[i],0,0))
+ != estatsOK)
+ {
+ return ok;
+ }
+ }
+ if ((ok = gmx_stats_get_ab(lsq,elsqWEIGHT_NONE,a,b,NULL,NULL,chi2,r)) != estatsOK)
+ {
+ return ok;
+ }
+
+ return estatsOK;
+ /*
+ double x,y,yx,xx,yy,sx,sy,chi2;
+
+ yx=xx=yy=sx=sy=0.0;
+ for (i=0; i<n; i++) {
+ if (xd != NULL) {
+ x = xd[i];
+ } else {
+ x = xr[i];
+ }
+ y = yr[i];
+
+ yx += y*x;
+ xx += x*x;
+ yy += y*y;
+ sx += x;
+ sy += y;
+ }
+ *a = (n*yx-sy*sx)/(n*xx-sx*sx);
+ *b = (sy-(*a)*sx)/n;
+ *r = sqrt((xx-sx*sx)/(yy-sy*sy));
+
+ chi2 = 0;
+ if (xd != NULL) {
+ for(i=0; i<n; i++)
+ chi2 += sqr(yr[i] - ((*a)*xd[i] + (*b)));
+ } else {
+ for(i=0; i<n; i++)
+ chi2 += sqr(yr[i] - ((*a)*xr[i] + (*b)));
+ }
+
+ if (n > 2)
+ return sqrt(chi2/(n-2));
+ else
+ return 0;
+ */
+}
+
+int lsq_y_ax_b(int n, real x[], real y[], real *a, real *b,real *r,real *chi2)
+{
+ return low_lsq_y_ax_b(n,x,NULL,y,a,b,r,chi2);
+}
+
+int lsq_y_ax_b_xdouble(int n, double x[], real y[], real *a, real *b,
+ real *r,real *chi2)
+{
+ return low_lsq_y_ax_b(n,NULL,x,y,a,b,r,chi2);
+}
+
+int lsq_y_ax_b_error(int n, real x[], real y[], real dy[],
+ real *a, real *b, real *da, real *db,
+ real *r,real *chi2)
+{
+ gmx_stats_t lsq;
+ int i,ok;
+
+ lsq = gmx_stats_init();
+ for(i=0; (i<n); i++)
+ {
+ if ((ok = gmx_stats_add_point(lsq,x[i],y[i],0,dy[i])) != estatsOK)
+ {
+ return ok;
+ }
+ }
+ if ((ok = gmx_stats_get_ab(lsq,elsqWEIGHT_Y,a,b,da,db,chi2,r)) != estatsOK)
+ {
+ return ok;
+ }
+ if ((ok = gmx_stats_done(lsq)) != estatsOK)
+ {
+ return ok;
+ }
+ sfree(lsq);
+
+ return estatsOK;
+ /*
+ double sxy,sxx,syy,sx,sy,w,s_2,dx2,dy2,mins;
+
+ sxy=sxx=syy=sx=sy=w=0.0;
+ mins = dy[0];
+ for(i=1; (i<n); i++)
+ mins = min(mins,dy[i]);
+ if (mins <= 0)
+ gmx_fatal(FARGS,"Zero or negative weigths in linear regression analysis");
+
+ for (i=0; i<n; i++) {
+ s_2 = sqr(1.0/dy[i]);
+ sxx += s_2*sqr(x[i]);
+ sxy += s_2*y[i]*x[i];
+ syy += s_2*sqr(y[i]);
+ sx += s_2*x[i];
+ sy += s_2*y[i];
+ w += s_2;
+ }
+ sxx = sxx/w;
+ sxy = sxy/w;
+ syy = syy/w;
+ sx = sx/w;
+ sy = sy/w;
+ dx2 = (sxx-sx*sx);
+ dy2 = (syy-sy*sy);
+ *a=(sxy-sy*sx)/dx2;
+ *b=(sy-(*a)*sx);
+
+ *chi2=0;
+ for(i=0; i<n; i++)
+ *chi2+=sqr((y[i]-((*a)*x[i]+(*b)))/dy[i]);
+ *chi2 = *chi2/w;
+
+ *da = sqrt(*chi2/((n-2)*dx2));
+ *db = *da*sqrt(sxx);
+ *r = *a*sqrt(dx2/dy2);
+
+ if (debug)
+ fprintf(debug,"sx = %g, sy = %g, sxy = %g, sxx = %g, w = %g\n"
+ "chi2 = %g, dx2 = %g\n",
+ sx,sy,sxy,sxx,w,*chi2,dx2);
+
+ if (n > 2)
+ *chi2 = sqrt(*chi2/(n-2));
+ else
+ *chi2 = 0;
+ */
+}
+
+
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+/* This file is completely threadsafe - keep it that way! */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef GMX_CRAY_XT3
+#undef HAVE_PWD_H
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <time.h>
+
+#include "typedefs.h"
+#include "smalloc.h"
+#include "gmx_fatal.h"
+#include "macros.h"
+#include "string2.h"
+#include "futil.h"
+
+int continuing(char *s)
+/* strip trailing spaces and if s ends with a CONTINUE remove that too.
+ * returns TRUE if s ends with a CONTINUE, FALSE otherwise.
+ */
+{
+ int sl;
+
+ rtrim(s);
+ sl = strlen(s);
+ if ((sl > 0) && (s[sl-1] == CONTINUE)) {
+ s[sl-1] = 0;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+char *fgets2(char *line, int n, FILE *stream)
+/* This routine reads a string from stream of max length n
+ * and zero terminated, without newlines
+ * line should be long enough (>= n)
+ */
+{
+ char *c;
+ if (fgets(line,n,stream) == NULL) {
+ return NULL;
+ }
+ if ((c=strchr(line,'\n')) != NULL) {
+ *c = '\0';
+ } else {
+ /* A line not ending in a newline can only occur at the end of a file,
+ * or because of n being too small.
+ * Since both cases occur very infrequently, we can check for EOF.
+ */
+ if (!gmx_eof(stream)) {
+ gmx_fatal(FARGS,"An input file contains a line longer than %d characters, while the buffer passed to fgets2 has size %d. The line starts with: '%20.20s'",n,n,line);
+ }
+ }
+ if ((c=strchr(line,'\r')) != NULL) {
+ *c = '\0';
+ }
+
+ return line;
+}
+
+void strip_comment (char *line)
+{
+ char *c;
+
+ if (!line)
+ return;
+
+ /* search for a comment mark and replace it by a zero */
+ if ((c = strchr(line,COMMENTSIGN)) != NULL)
+ (*c) = 0;
+}
+
+void upstring (char *str)
+{
+ int i;
+
+ for (i=0; (i < (int)strlen(str)); i++)
+ str[i] = toupper(str[i]);
+}
+
+void ltrim (char *str)
+{
+ char *tr;
+ int i,c;
+
+ if (NULL == str)
+ return;
+
+ c = 0;
+ while (('\0' != str[c]) && isspace(str[c]))
+ c++;
+ if (c > 0)
+ {
+ for(i=c; ('\0' != str[i]); i++)
+ str[i-c] = str[i];
+ str[i-c] = '\0';
+ }
+}
+
+void rtrim (char *str)
+{
+ int nul;
+
+ if (NULL == str)
+ return;
+
+ nul = strlen(str)-1;
+ while ((nul > 0) && ((str[nul] == ' ') || (str[nul] == '\t')) ) {
+ str[nul] = '\0';
+ nul--;
+ }
+}
+
+void trim (char *str)
+{
+ ltrim (str);
+ rtrim (str);
+}
+
+char *
+gmx_ctime_r(const time_t *clock,char *buf, int n)
+{
+ char tmpbuf[STRLEN];
+
+#if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
+ /* Windows */
+ ctime_s( tmpbuf, STRLEN, clock );
+#else
+ ctime_r(clock,tmpbuf);
+#endif
+ strncpy(buf,tmpbuf,n-1);
+ buf[n-1]='\0';
+
+ return buf;
+}
+
+void nice_header (FILE *out,const char *fn)
+{
+ const char *unk = "onbekend";
+ time_t clock;
+ char *user=NULL;
+ int gh;
+ uid_t uid;
+ char buf[256];
+ char timebuf[STRLEN];
+#ifdef HAVE_PWD_H
+ struct passwd *pw;
+#endif
+
+ /* Print a nice header above the file */
+ time(&clock);
+ fprintf (out,"%c\n",COMMENTSIGN);
+ fprintf (out,"%c\tFile '%s' was generated\n",COMMENTSIGN,fn ? fn : unk);
+
+#ifdef HAVE_PWD_H
+ uid = getuid();
+ pw = getpwuid(uid);
+ gh = gethostname(buf,255);
+ user= pw->pw_name;
+#else
+ uid = 0;
+ gh = -1;
+#endif
+
+ gmx_ctime_r(&clock,timebuf,STRLEN);
+ fprintf (out,"%c\tBy user: %s (%d)\n",COMMENTSIGN,
+ user ? user : unk,(int) uid);
+ fprintf(out,"%c\tOn host: %s\n",COMMENTSIGN,(gh == 0) ? buf : unk);
+
+ fprintf (out,"%c\tAt date: %s",COMMENTSIGN,timebuf);
+ fprintf (out,"%c\n",COMMENTSIGN);
+}
+
+int gmx_strcasecmp_min(const char *str1, const char *str2)
+{
+ char ch1,ch2;
+
+ do
+ {
+ do
+ ch1=toupper(*(str1++));
+ while ((ch1=='-') || (ch1=='_'));
+ do
+ ch2=toupper(*(str2++));
+ while ((ch2=='-') || (ch2=='_'));
+ if (ch1!=ch2) return (ch1-ch2);
+ }
+ while (ch1);
+ return 0;
+}
+
+int gmx_strncasecmp_min(const char *str1, const char *str2, int n)
+{
+ char ch1,ch2;
+ char *stri1, *stri2;
+
+ stri1=(char *)str1;
+ stri2=(char *)str2;
+ do
+ {
+ do
+ ch1=toupper(*(str1++));
+ while ((ch1=='-') || (ch1=='_'));
+ do
+ ch2=toupper(*(str2++));
+ while ((ch2=='-') || (ch2=='_'));
+ if (ch1!=ch2) return (ch1-ch2);
+ }
+ while (ch1 && (str1-stri1<n) && (str2-stri2<n));
+ return 0;
+}
+
+int gmx_strcasecmp(const char *str1, const char *str2)
+{
+ char ch1,ch2;
+
+ do
+ {
+ ch1=toupper(*(str1++));
+ ch2=toupper(*(str2++));
+ if (ch1!=ch2) return (ch1-ch2);
+ }
+ while (ch1);
+ return 0;
+}
+
+int gmx_strncasecmp(const char *str1, const char *str2, int n)
+{
+ char ch1,ch2;
+
+ if(n==0)
+ return 0;
+
+ do
+ {
+ ch1=toupper(*(str1++));
+ ch2=toupper(*(str2++));
+ if (ch1!=ch2) return (ch1-ch2);
+ n--;
+ }
+ while (ch1 && n);
+ return 0;
+}
+
+char *gmx_strdup(const char *src)
+{
+ char *dest;
+
+ snew(dest,strlen(src)+1);
+ strcpy(dest,src);
+
+ return dest;
+}
+
+char *
+gmx_strndup(const char *src, int n)
+{
+ int len;
+ char *dest;
+
+ len = strlen(src);
+ if (len > n)
+ {
+ len = n;
+ }
+ snew(dest, len+1);
+ strncpy(dest, src, len);
+ dest[len] = 0;
+ return dest;
+}
+
+/*!
+ * \param[in] pattern Pattern to match against.
+ * \param[in] str String to match.
+ * \returns 0 on match, GMX_NO_WCMATCH if there is no match.
+ *
+ * Matches \p str against \p pattern, which may contain * and ? wildcards.
+ * All other characters are matched literally.
+ * Currently, it is not possible to match literal * or ?.
+ */
+int
+gmx_wcmatch(const char *pattern, const char *str)
+{
+ while (*pattern)
+ {
+ if (*pattern == '*')
+ {
+ /* Skip multiple wildcards in a sequence */
+ while (*pattern == '*' || *pattern == '?')
+ {
+ ++pattern;
+ /* For ?, we need to check that there are characters left
+ * in str. */
+ if (*pattern == '?')
+ {
+ if (*str == 0)
+ {
+ return GMX_NO_WCMATCH;
+ }
+ else
+ {
+ ++str;
+ }
+ }
+ }
+ /* If the pattern ends after the star, we have a match */
+ if (*pattern == 0)
+ {
+ return 0;
+ }
+ /* Match the rest against each possible suffix of str */
+ while (*str)
+ {
+ /* Only do the recursive call if the first character
+ * matches. We don't have to worry about wildcards here,
+ * since we have processed them above. */
+ if (*pattern == *str)
+ {
+ int rc;
+ /* Match the suffix, and return if a match or an error */
+ rc = gmx_wcmatch(pattern, str);
+ if (rc != GMX_NO_WCMATCH)
+ {
+ return rc;
+ }
+ }
+ ++str;
+ }
+ /* If no suffix of str matches, we don't have a match */
+ return GMX_NO_WCMATCH;
+ }
+ else if ((*pattern == '?' && *str != 0) || *pattern == *str)
+ {
+ ++str;
+ }
+ else
+ {
+ return GMX_NO_WCMATCH;
+ }
+ ++pattern;
+ }
+ /* When the pattern runs out, we have a match if the string has ended. */
+ return (*str == 0) ? 0 : GMX_NO_WCMATCH;
+}
+
+char *wrap_lines(const char *buf,int line_width, int indent,gmx_bool bIndentFirst)
+{
+ char *b2;
+ int i,i0,i2,j,b2len,lspace=0,l2space=0;
+ gmx_bool bFirst,bFitsOnLine;
+
+ /* characters are copied from buf to b2 with possible spaces changed
+ * into newlines and extra space added for indentation.
+ * i indexes buf (source buffer) and i2 indexes b2 (destination buffer)
+ * i0 points to the beginning of the current line (in buf, source)
+ * lspace and l2space point to the last space on the current line
+ * bFirst is set to prevent indentation of first line
+ * bFitsOnLine says if the first space occurred before line_width, if
+ * that is not the case, we have a word longer than line_width which
+ * will also not fit on the next line, so we might as well keep it on
+ * the current line (where it also won't fit, but looks better)
+ */
+
+ b2=NULL;
+ b2len=strlen(buf)+1+indent;
+ snew(b2,b2len);
+ i0=i2=0;
+ if (bIndentFirst)
+ for(i2=0; (i2<indent); i2++)
+ b2[i2] = ' ';
+ bFirst=TRUE;
+ do {
+ l2space = -1;
+ /* find the last space before end of line */
+ for(i=i0; ((i-i0 < line_width) || (l2space==-1)) && (buf[i]); i++) {
+ b2[i2++] = buf[i];
+ /* remember the position of a space */
+ if (buf[i] == ' ') {
+ lspace = i;
+ l2space = i2-1;
+ }
+ /* if we have a newline before the line is full, reset counters */
+ if (buf[i]=='\n' && buf[i+1]) {
+ i0=i+1;
+ b2len+=indent;
+ srenew(b2, b2len);
+ /* add indentation after the newline */
+ for(j=0; (j<indent); j++)
+ b2[i2++]=' ';
+ }
+ }
+ /* If we are at the last newline, copy it */
+ if (buf[i]=='\n' && !buf[i+1]) {
+ b2[i2++] = buf[i++];
+ }
+ /* if we're not at the end of the string */
+ if (buf[i]) {
+ /* check if one word does not fit on the line */
+ bFitsOnLine = (i-i0 <= line_width);
+ /* reset line counters to just after the space */
+ i0 = lspace+1;
+ i2 = l2space+1;
+ /* if the words fit on the line, and we're beyond the indentation part */
+ if ( (bFitsOnLine) && (l2space >= indent) ) {
+ /* start a new line */
+ b2[l2space] = '\n';
+ /* and add indentation */
+ if (indent) {
+ if (bFirst) {
+ line_width-=indent;
+ bFirst=FALSE;
+ }
+ b2len+=indent;
+ srenew(b2, b2len);
+ for(j=0; (j<indent); j++)
+ b2[i2++]=' ';
+ /* no extra spaces after indent; */
+ while(buf[i0]==' ')
+ i0++;
+ }
+ }
+ }
+ } while (buf[i]);
+ b2[i2] = '\0';
+
+ return b2;
+}
+
+char **split(char sep,const char *str)
+{
+ char **ptr = NULL;
+ int n,nn,nptr = 0;
+
+ if (str == NULL)
+ return NULL;
+ nn = strlen(str);
+ for(n=0; (n<nn); n++)
+ if (str[n] == sep)
+ nptr++;
+ snew(ptr,nptr+2);
+ nptr = 0;
+ while (*str != '\0') {
+ while ((*str != '\0') && (*str == sep))
+ str++;
+ if (*str != '\0') {
+ snew(ptr[nptr],1+strlen(str));
+ n = 0;
+ while ((*str != '\0') && (*str != sep)) {
+ ptr[nptr][n] = *str;
+ str++;
+ n++;
+ }
+ ptr[nptr][n] = '\0';
+ nptr++;
+ }
+ }
+ ptr[nptr] = NULL;
+
+ return ptr;
+}
+
+
+gmx_large_int_t
+str_to_large_int_t(const char *str, char **endptr)
+{
+ int sign = 1;
+ gmx_large_int_t val = 0;
+ char ch;
+ const char *p;
+
+ p = str;
+ if(p==NULL)
+ {
+ *endptr=NULL;
+ return 0;
+ }
+
+ /* Strip off initial white space */
+ while(isspace(*p))
+ {
+ p++;
+ }
+ /* Conform to ISO C99 - return original pointer if string does not contain a number */
+ if(*str=='\0')
+ {
+ *endptr=(char *)str;
+ }
+
+ if(*p=='-')
+ {
+ p++;
+ sign *= -1;
+ }
+
+ while( ((ch=*p) != '\0') && isdigit(ch) )
+ {
+ /* Important to add sign here, so we dont overflow in final multiplication */
+ ch = (ch-'0')*sign;
+ val = val*10 + ch;
+ if(ch != val%10)
+ {
+ /* Some sort of overflow has occured, set endptr to original string */
+ *endptr=(char *)str;
+ errno = ERANGE;
+ return(0);
+ }
+ p++;
+ }
+
+ *endptr=(char *)p;
+
+ return val;
+}
+
+char *gmx_strsep(char **stringp, const char *delim)
+{
+ char *ret;
+ int len=strlen(delim);
+ int i,j=0;
+ int found=0;
+
+ if (! *stringp)
+ return NULL;
+ ret=*stringp;
+ do
+ {
+ if ( (*stringp)[j] == '\0')
+ {
+ found=1;
+ *stringp=NULL;
+ break;
+ }
+ for (i=0;i<len;i++)
+ {
+ if ( (*stringp)[j]==delim[i])
+ {
+ (*stringp)[j]='\0';
+ *stringp=*stringp+j+1;
+ found=1;
+ break;
+ }
+ }
+ j++;
+ } while (!found);
+
+ return ret;
+}
+
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This file is completely threadsafe - keep it that way! */
+#ifdef GMX_THREADS
+#include <thread_mpi.h>
+#endif
+
+
+#include <ctype.h>
+#include "sysstuff.h"
+#include "smalloc.h"
+#include "string2.h"
+#include "gmx_fatal.h"
+#include "macros.h"
+#include "names.h"
+#include "symtab.h"
+#include "futil.h"
+#include "filenm.h"
+#include "gmxfio.h"
+#include "topsort.h"
+#include "tpxio.h"
+#include "txtdump.h"
+#include "confio.h"
+#include "atomprop.h"
+#include "copyrite.h"
+#include "vec.h"
+#include "mtop_util.h"
+
+/* This number should be increased whenever the file format changes! */
+static const int tpx_version = 74;
+
+/* This number should only be increased when you edit the TOPOLOGY section
+ * of the tpx format. This way we can maintain forward compatibility too
+ * for all analysis tools and/or external programs that only need to
+ * know the atom/residue names, charges, and bond connectivity.
+ *
+ * It first appeared in tpx version 26, when I also moved the inputrecord
+ * to the end of the tpx file, so we can just skip it if we only
+ * want the topology.
+ */
+static const int tpx_generation = 23;
+
+/* This number should be the most recent backwards incompatible version
+ * I.e., if this number is 9, we cannot read tpx version 9 with this code.
+ */
+static const int tpx_incompatible_version = 9;
+
+
+
+/* Struct used to maintain tpx compatibility when function types are added */
+typedef struct {
+ int fvnr; /* file version number in which the function type first appeared */
+ int ftype; /* function type */
+} t_ftupd;
+
+/*
+ *The entries should be ordered in:
+ * 1. ascending file version number
+ * 2. ascending function type number
+ */
+/*static const t_ftupd ftupd[] = {
+ { 20, F_CUBICBONDS },
+ { 20, F_CONNBONDS },
+ { 20, F_HARMONIC },
+ { 20, F_EQM, },
+ { 22, F_DISRESVIOL },
+ { 22, F_ORIRES },
+ { 22, F_ORIRESDEV },
+ { 26, F_FOURDIHS },
+ { 26, F_PIDIHS },
+ { 26, F_DIHRES },
+ { 26, F_DIHRESVIOL },
+ { 30, F_CROSS_BOND_BONDS },
+ { 30, F_CROSS_BOND_ANGLES },
+ { 30, F_UREY_BRADLEY },
+ { 30, F_POLARIZATION }
+ };*/
+
+/*
+ *The entries should be ordered in:
+ * 1. ascending function type number
+ * 2. ascending file version number
+ */
+static const t_ftupd ftupd[] = {
+ { 20, F_CUBICBONDS },
+ { 20, F_CONNBONDS },
+ { 20, F_HARMONIC },
+ { 34, F_FENEBONDS },
+ { 43, F_TABBONDS },
+ { 43, F_TABBONDSNC },
+ { 70, F_RESTRBONDS },
+ { 30, F_CROSS_BOND_BONDS },
+ { 30, F_CROSS_BOND_ANGLES },
+ { 30, F_UREY_BRADLEY },
+ { 34, F_QUARTIC_ANGLES },
+ { 43, F_TABANGLES },
+ { 26, F_FOURDIHS },
+ { 26, F_PIDIHS },
+ { 43, F_TABDIHS },
+ { 65, F_CMAP },
+ { 60, F_GB12 },
+ { 61, F_GB13 },
+ { 61, F_GB14 },
+ { 72, F_GBPOL },
+ { 72, F_NPSOLVATION },
+ { 41, F_LJC14_Q },
+ { 41, F_LJC_PAIRS_NB },
+ { 32, F_BHAM_LR },
+ { 32, F_RF_EXCL },
+ { 32, F_COUL_RECIP },
+ { 46, F_DPD },
+ { 30, F_POLARIZATION },
+ { 36, F_THOLE_POL },
+ { 22, F_DISRESVIOL },
+ { 22, F_ORIRES },
+ { 22, F_ORIRESDEV },
+ { 26, F_DIHRES },
+ { 26, F_DIHRESVIOL },
+ { 49, F_VSITE4FDN },
+ { 50, F_VSITEN },
+ { 46, F_COM_PULL },
+ { 20, F_EQM },
+ { 46, F_ECONSERVED },
+ { 69, F_VTEMP },
+ { 66, F_PDISPCORR },
+ { 54, F_DHDL_CON },
+};
+#define NFTUPD asize(ftupd)
+
+/* Needed for backward compatibility */
+#define MAXNODES 256
+
+static void _do_section(t_fileio *fio,int key,gmx_bool bRead,const char *src,
+ int line)
+{
+ char buf[STRLEN];
+ gmx_bool bDbg;
+
+ if (gmx_fio_getftp(fio) == efTPA) {
+ if (!bRead) {
+ gmx_fio_write_string(fio,itemstr[key]);
+ bDbg = gmx_fio_getdebug(fio);
+ gmx_fio_setdebug(fio,FALSE);
+ gmx_fio_write_string(fio,comment_str[key]);
+ gmx_fio_setdebug(fio,bDbg);
+ }
+ else {
+ if (gmx_fio_getdebug(fio))
+ fprintf(stderr,"Looking for section %s (%s, %d)",
+ itemstr[key],src,line);
+
+ do {
+ gmx_fio_do_string(fio,buf);
+ } while ((gmx_strcasecmp(buf,itemstr[key]) != 0));
+
+ if (gmx_strcasecmp(buf,itemstr[key]) != 0)
+ gmx_fatal(FARGS,"\nCould not find section heading %s",itemstr[key]);
+ else if (gmx_fio_getdebug(fio))
+ fprintf(stderr," and found it\n");
+ }
+ }
+}
+
+#define do_section(fio,key,bRead) _do_section(fio,key,bRead,__FILE__,__LINE__)
+
+/**************************************************************
+ *
+ * Now the higer level routines that do io of the structures and arrays
+ *
+ **************************************************************/
+static void do_pullgrp(t_fileio *fio, t_pullgrp *pgrp, gmx_bool bRead,
+ int file_version)
+{
+ gmx_bool bDum=TRUE;
+ int i;
+
+ gmx_fio_do_int(fio,pgrp->nat);
+ if (bRead)
+ snew(pgrp->ind,pgrp->nat);
+ bDum=gmx_fio_ndo_int(fio,pgrp->ind,pgrp->nat);
+ gmx_fio_do_int(fio,pgrp->nweight);
+ if (bRead)
+ snew(pgrp->weight,pgrp->nweight);
+ bDum=gmx_fio_ndo_real(fio,pgrp->weight,pgrp->nweight);
+ gmx_fio_do_int(fio,pgrp->pbcatom);
+ gmx_fio_do_rvec(fio,pgrp->vec);
+ gmx_fio_do_rvec(fio,pgrp->init);
+ gmx_fio_do_real(fio,pgrp->rate);
+ gmx_fio_do_real(fio,pgrp->k);
+ if (file_version >= 56) {
+ gmx_fio_do_real(fio,pgrp->kB);
+ } else {
+ pgrp->kB = pgrp->k;
+ }
+}
+
+static void do_pull(t_fileio *fio, t_pull *pull,gmx_bool bRead, int file_version)
+{
+ int g;
+
+ gmx_fio_do_int(fio,pull->ngrp);
+ gmx_fio_do_int(fio,pull->eGeom);
+ gmx_fio_do_ivec(fio,pull->dim);
+ gmx_fio_do_real(fio,pull->cyl_r1);
+ gmx_fio_do_real(fio,pull->cyl_r0);
+ gmx_fio_do_real(fio,pull->constr_tol);
+ gmx_fio_do_int(fio,pull->nstxout);
+ gmx_fio_do_int(fio,pull->nstfout);
+ if (bRead)
+ snew(pull->grp,pull->ngrp+1);
+ for(g=0; g<pull->ngrp+1; g++)
+ do_pullgrp(fio,&pull->grp[g],bRead,file_version);
+}
+
+
+static void do_rotgrp(t_fileio *fio, t_rotgrp *rotg,gmx_bool bRead, int file_version)
+{
+ gmx_bool bDum=TRUE;
+ int i;
+
+ gmx_fio_do_int(fio,rotg->eType);
+ gmx_fio_do_int(fio,rotg->bMassW);
+ gmx_fio_do_int(fio,rotg->nat);
+ if (bRead)
+ snew(rotg->ind,rotg->nat);
+ gmx_fio_ndo_int(fio,rotg->ind,rotg->nat);
+ if (bRead)
+ snew(rotg->x_ref,rotg->nat);
+ gmx_fio_ndo_rvec(fio,rotg->x_ref,rotg->nat);
+ gmx_fio_do_rvec(fio,rotg->vec);
+ gmx_fio_do_rvec(fio,rotg->pivot);
+ gmx_fio_do_real(fio,rotg->rate);
+ gmx_fio_do_real(fio,rotg->k);
+ gmx_fio_do_real(fio,rotg->slab_dist);
+ gmx_fio_do_real(fio,rotg->min_gaussian);
+ gmx_fio_do_real(fio,rotg->eps);
+ gmx_fio_do_int(fio,rotg->eFittype);
+}
+
+static void do_rot(t_fileio *fio, t_rot *rot,gmx_bool bRead, int file_version)
+{
+ int g;
+
+ gmx_fio_do_int(fio,rot->ngrp);
+ gmx_fio_do_int(fio,rot->nstrout);
+ gmx_fio_do_int(fio,rot->nstsout);
+ if (bRead)
+ snew(rot->grp,rot->ngrp);
+ for(g=0; g<rot->ngrp; g++)
+ do_rotgrp(fio, &rot->grp[g],bRead,file_version);
+}
+
+
+static void do_inputrec(t_fileio *fio, t_inputrec *ir,gmx_bool bRead,
+ int file_version, real *fudgeQQ)
+{
+ int i,j,k,*tmp,idum=0;
+ gmx_bool bDum=TRUE;
+ real rdum,bd_temp;
+ rvec vdum;
+ gmx_bool bSimAnn;
+ real zerotemptime,finish_t,init_temp,finish_temp;
+
+ if (file_version != tpx_version)
+ {
+ /* Give a warning about features that are not accessible */
+ fprintf(stderr,"Note: tpx file_version %d, software version %d\n",
+ file_version,tpx_version);
+ }
+
+ if (bRead)
+ {
+ init_inputrec(ir);
+ }
+
+ if (file_version == 0)
+ {
+ return;
+ }
+
+ /* Basic inputrec stuff */
+ gmx_fio_do_int(fio,ir->eI);
+ if (file_version >= 62) {
+ gmx_fio_do_gmx_large_int(fio, ir->nsteps);
+ } else {
+ gmx_fio_do_int(fio,idum);
+ ir->nsteps = idum;
+ }
+ if(file_version > 25) {
+ if (file_version >= 62) {
+ gmx_fio_do_gmx_large_int(fio, ir->init_step);
+ } else {
+ gmx_fio_do_int(fio,idum);
+ ir->init_step = idum;
+ }
+ } else {
+ ir->init_step=0;
+ }
+
+ if(file_version >= 58)
+ gmx_fio_do_int(fio,ir->simulation_part);
+ else
+ ir->simulation_part=1;
+
+ if (file_version >= 67) {
+ gmx_fio_do_int(fio,ir->nstcalcenergy);
+ } else {
+ ir->nstcalcenergy = 1;
+ }
+ if (file_version < 53) {
+ /* The pbc info has been moved out of do_inputrec,
+ * since we always want it, also without reading the inputrec.
+ */
+ gmx_fio_do_int(fio,ir->ePBC);
+ if ((file_version <= 15) && (ir->ePBC == 2))
+ ir->ePBC = epbcNONE;
+ if (file_version >= 45) {
+ gmx_fio_do_int(fio,ir->bPeriodicMols);
+ } else {
+ if (ir->ePBC == 2) {
+ ir->ePBC = epbcXYZ;
+ ir->bPeriodicMols = TRUE;
+ } else {
+ ir->bPeriodicMols = FALSE;
+ }
+ }
+ }
+ gmx_fio_do_int(fio,ir->ns_type);
+ gmx_fio_do_int(fio,ir->nstlist);
+ gmx_fio_do_int(fio,ir->ndelta);
+ if (file_version < 41) {
+ gmx_fio_do_int(fio,idum);
+ gmx_fio_do_int(fio,idum);
+ }
+ if (file_version >= 45)
+ gmx_fio_do_real(fio,ir->rtpi);
+ else
+ ir->rtpi = 0.05;
+ gmx_fio_do_int(fio,ir->nstcomm);
+ if (file_version > 34)
+ gmx_fio_do_int(fio,ir->comm_mode);
+ else if (ir->nstcomm < 0)
+ ir->comm_mode = ecmANGULAR;
+ else
+ ir->comm_mode = ecmLINEAR;
+ ir->nstcomm = abs(ir->nstcomm);
+
+ if(file_version > 25)
+ gmx_fio_do_int(fio,ir->nstcheckpoint);
+ else
+ ir->nstcheckpoint=0;
+
+ gmx_fio_do_int(fio,ir->nstcgsteep);
+
+ if(file_version>=30)
+ gmx_fio_do_int(fio,ir->nbfgscorr);
+ else if (bRead)
+ ir->nbfgscorr = 10;
+
+ gmx_fio_do_int(fio,ir->nstlog);
+ gmx_fio_do_int(fio,ir->nstxout);
+ gmx_fio_do_int(fio,ir->nstvout);
+ gmx_fio_do_int(fio,ir->nstfout);
+ gmx_fio_do_int(fio,ir->nstenergy);
+ gmx_fio_do_int(fio,ir->nstxtcout);
+ if (file_version >= 59) {
+ gmx_fio_do_double(fio,ir->init_t);
+ gmx_fio_do_double(fio,ir->delta_t);
+ } else {
+ gmx_fio_do_real(fio,rdum);
+ ir->init_t = rdum;
+ gmx_fio_do_real(fio,rdum);
+ ir->delta_t = rdum;
+ }
+ gmx_fio_do_real(fio,ir->xtcprec);
+ if (file_version < 19) {
+ gmx_fio_do_int(fio,idum);
+ gmx_fio_do_int(fio,idum);
+ }
+ if(file_version < 18)
+ gmx_fio_do_int(fio,idum);
+ gmx_fio_do_real(fio,ir->rlist);
+ if (file_version >= 67) {
+ gmx_fio_do_real(fio,ir->rlistlong);
+ }
+ gmx_fio_do_int(fio,ir->coulombtype);
+ if (file_version < 32 && ir->coulombtype == eelRF)
+ ir->coulombtype = eelRF_NEC;
+ gmx_fio_do_real(fio,ir->rcoulomb_switch);
+ gmx_fio_do_real(fio,ir->rcoulomb);
+ gmx_fio_do_int(fio,ir->vdwtype);
+ gmx_fio_do_real(fio,ir->rvdw_switch);
+ gmx_fio_do_real(fio,ir->rvdw);
+ if (file_version < 67) {
+ ir->rlistlong = max_cutoff(ir->rlist,max_cutoff(ir->rvdw,ir->rcoulomb));
+ }
+ gmx_fio_do_int(fio,ir->eDispCorr);
+ gmx_fio_do_real(fio,ir->epsilon_r);
+ if (file_version >= 37) {
+ gmx_fio_do_real(fio,ir->epsilon_rf);
+ } else {
+ if (EEL_RF(ir->coulombtype)) {
+ ir->epsilon_rf = ir->epsilon_r;
+ ir->epsilon_r = 1.0;
+ } else {
+ ir->epsilon_rf = 1.0;
+ }
+ }
+ if (file_version >= 29)
+ gmx_fio_do_real(fio,ir->tabext);
+ else
+ ir->tabext=1.0;
+
+ if(file_version > 25) {
+ gmx_fio_do_int(fio,ir->gb_algorithm);
+ gmx_fio_do_int(fio,ir->nstgbradii);
+ gmx_fio_do_real(fio,ir->rgbradii);
+ gmx_fio_do_real(fio,ir->gb_saltconc);
+ gmx_fio_do_int(fio,ir->implicit_solvent);
+ } else {
+ ir->gb_algorithm=egbSTILL;
+ ir->nstgbradii=1;
+ ir->rgbradii=1.0;
+ ir->gb_saltconc=0;
+ ir->implicit_solvent=eisNO;
+ }
+ if(file_version>=55)
+ {
+ gmx_fio_do_real(fio,ir->gb_epsilon_solvent);
+ gmx_fio_do_real(fio,ir->gb_obc_alpha);
+ gmx_fio_do_real(fio,ir->gb_obc_beta);
+ gmx_fio_do_real(fio,ir->gb_obc_gamma);
+ if(file_version>=60)
+ {
+ gmx_fio_do_real(fio,ir->gb_dielectric_offset);
+ gmx_fio_do_int(fio,ir->sa_algorithm);
+ }
+ else
+ {
+ ir->gb_dielectric_offset = 0.009;
+ ir->sa_algorithm = esaAPPROX;
+ }
+ gmx_fio_do_real(fio,ir->sa_surface_tension);
+
+ /* Override sa_surface_tension if it is not changed in the mpd-file */
+ if(ir->sa_surface_tension<0)
+ {
+ if(ir->gb_algorithm==egbSTILL)
+ {
+ ir->sa_surface_tension = 0.0049 * 100 * CAL2JOULE;
+ }
+ else if(ir->gb_algorithm==egbHCT || ir->gb_algorithm==egbOBC)
+ {
+ ir->sa_surface_tension = 0.0054 * 100 * CAL2JOULE;
+ }
+ }
+
+ }
+ else
+ {
+ /* Better use sensible values than insane (0.0) ones... */
+ ir->gb_epsilon_solvent = 80;
+ ir->gb_obc_alpha = 1.0;
+ ir->gb_obc_beta = 0.8;
+ ir->gb_obc_gamma = 4.85;
+ ir->sa_surface_tension = 2.092;
+ }
+
+
+ gmx_fio_do_int(fio,ir->nkx);
+ gmx_fio_do_int(fio,ir->nky);
+ gmx_fio_do_int(fio,ir->nkz);
+ gmx_fio_do_int(fio,ir->pme_order);
+ gmx_fio_do_real(fio,ir->ewald_rtol);
+
+ if (file_version >=24)
+ gmx_fio_do_int(fio,ir->ewald_geometry);
+ else
+ ir->ewald_geometry=eewg3D;
+
+ if (file_version <=17) {
+ ir->epsilon_surface=0;
+ if (file_version==17)
+ gmx_fio_do_int(fio,idum);
+ }
+ else
+ gmx_fio_do_real(fio,ir->epsilon_surface);
+
+ gmx_fio_do_gmx_bool(fio,ir->bOptFFT);
+
+ gmx_fio_do_gmx_bool(fio,ir->bContinuation);
+ gmx_fio_do_int(fio,ir->etc);
+ /* before version 18, ir->etc was a gmx_bool (ir->btc),
+ * but the values 0 and 1 still mean no and
+ * berendsen temperature coupling, respectively.
+ */
+ if (file_version >= 71)
+ {
+ gmx_fio_do_int(fio,ir->nsttcouple);
+ }
+ else
+ {
+ ir->nsttcouple = ir->nstcalcenergy;
+ }
+ if (file_version <= 15)
+ {
+ gmx_fio_do_int(fio,idum);
+ }
+ if (file_version <=17)
+ {
+ gmx_fio_do_int(fio,ir->epct);
+ if (file_version <= 15)
+ {
+ if (ir->epct == 5)
+ {
+ ir->epct = epctSURFACETENSION;
+ }
+ gmx_fio_do_int(fio,idum);
+ }
+ ir->epct -= 1;
+ /* we have removed the NO alternative at the beginning */
+ if(ir->epct==-1)
+ {
+ ir->epc=epcNO;
+ ir->epct=epctISOTROPIC;
+ }
+ else
+ {
+ ir->epc=epcBERENDSEN;
+ }
+ }
+ else
+ {
+ gmx_fio_do_int(fio,ir->epc);
+ gmx_fio_do_int(fio,ir->epct);
+ }
+ if (file_version >= 71)
+ {
+ gmx_fio_do_int(fio,ir->nstpcouple);
+ }
+ else
+ {
+ ir->nstpcouple = ir->nstcalcenergy;
+ }
+ gmx_fio_do_real(fio,ir->tau_p);
+ if (file_version <= 15) {
+ gmx_fio_do_rvec(fio,vdum);
+ clear_mat(ir->ref_p);
+ for(i=0; i<DIM; i++)
+ ir->ref_p[i][i] = vdum[i];
+ } else {
+ gmx_fio_do_rvec(fio,ir->ref_p[XX]);
+ gmx_fio_do_rvec(fio,ir->ref_p[YY]);
+ gmx_fio_do_rvec(fio,ir->ref_p[ZZ]);
+ }
+ if (file_version <= 15) {
+ gmx_fio_do_rvec(fio,vdum);
+ clear_mat(ir->compress);
+ for(i=0; i<DIM; i++)
+ ir->compress[i][i] = vdum[i];
+ }
+ else {
+ gmx_fio_do_rvec(fio,ir->compress[XX]);
+ gmx_fio_do_rvec(fio,ir->compress[YY]);
+ gmx_fio_do_rvec(fio,ir->compress[ZZ]);
+ }
+ if (file_version >= 47) {
+ gmx_fio_do_int(fio,ir->refcoord_scaling);
+ gmx_fio_do_rvec(fio,ir->posres_com);
+ gmx_fio_do_rvec(fio,ir->posres_comB);
+ } else {
+ ir->refcoord_scaling = erscNO;
+ clear_rvec(ir->posres_com);
+ clear_rvec(ir->posres_comB);
+ }
+ if(file_version > 25)
+ gmx_fio_do_int(fio,ir->andersen_seed);
+ else
+ ir->andersen_seed=0;
+
+ if(file_version < 26) {
+ gmx_fio_do_gmx_bool(fio,bSimAnn);
+ gmx_fio_do_real(fio,zerotemptime);
+ }
+
+ if (file_version < 37)
+ gmx_fio_do_real(fio,rdum);
+
+ gmx_fio_do_real(fio,ir->shake_tol);
+ if (file_version < 54)
+ gmx_fio_do_real(fio,*fudgeQQ);
+ gmx_fio_do_int(fio,ir->efep);
+ if (file_version <= 14 && ir->efep > efepNO)
+ ir->efep = efepYES;
+ if (file_version >= 59) {
+ gmx_fio_do_double(fio, ir->init_lambda);
+ gmx_fio_do_double(fio, ir->delta_lambda);
+ } else {
+ gmx_fio_do_real(fio,rdum);
+ ir->init_lambda = rdum;
+ gmx_fio_do_real(fio,rdum);
+ ir->delta_lambda = rdum;
+ }
+ if (file_version >= 64) {
+ gmx_fio_do_int(fio,ir->n_flambda);
+ if (bRead) {
+ snew(ir->flambda,ir->n_flambda);
+ }
+ bDum=gmx_fio_ndo_double(fio,ir->flambda,ir->n_flambda);
+ } else {
+ ir->n_flambda = 0;
+ ir->flambda = NULL;
+ }
+ if (file_version >= 13)
+ gmx_fio_do_real(fio,ir->sc_alpha);
+ else
+ ir->sc_alpha = 0;
+ if (file_version >= 38)
+ gmx_fio_do_int(fio,ir->sc_power);
+ else
+ ir->sc_power = 2;
+ if (file_version >= 15)
+ gmx_fio_do_real(fio,ir->sc_sigma);
+ else
+ ir->sc_sigma = 0.3;
+ if (bRead)
+ {
+ if (file_version >= 71)
+ {
+ ir->sc_sigma_min = ir->sc_sigma;
+ }
+ else
+ {
+ ir->sc_sigma_min = 0;
+ }
+ }
+ if (file_version >= 64) {
+ gmx_fio_do_int(fio,ir->nstdhdl);
+ } else {
+ ir->nstdhdl = 1;
+ }
+
+ if (file_version >= 73)
+ {
+ gmx_fio_do_int(fio, ir->separate_dhdl_file);
+ gmx_fio_do_int(fio, ir->dhdl_derivatives);
+ }
+ else
+ {
+ ir->separate_dhdl_file = sepdhdlfileYES;
+ ir->dhdl_derivatives = dhdlderivativesYES;
+ }
+
+ if (file_version >= 71)
+ {
+ gmx_fio_do_int(fio,ir->dh_hist_size);
+ gmx_fio_do_double(fio,ir->dh_hist_spacing);
+ }
+ else
+ {
+ ir->dh_hist_size = 0;
+ ir->dh_hist_spacing = 0.1;
+ }
+ if (file_version >= 57) {
+ gmx_fio_do_int(fio,ir->eDisre);
+ }
+ gmx_fio_do_int(fio,ir->eDisreWeighting);
+ if (file_version < 22) {
+ if (ir->eDisreWeighting == 0)
+ ir->eDisreWeighting = edrwEqual;
+ else
+ ir->eDisreWeighting = edrwConservative;
+ }
+ gmx_fio_do_gmx_bool(fio,ir->bDisreMixed);
+ gmx_fio_do_real(fio,ir->dr_fc);
+ gmx_fio_do_real(fio,ir->dr_tau);
+ gmx_fio_do_int(fio,ir->nstdisreout);
+ if (file_version >= 22) {
+ gmx_fio_do_real(fio,ir->orires_fc);
+ gmx_fio_do_real(fio,ir->orires_tau);
+ gmx_fio_do_int(fio,ir->nstorireout);
+ } else {
+ ir->orires_fc = 0;
+ ir->orires_tau = 0;
+ ir->nstorireout = 0;
+ }
+ if(file_version >= 26) {
+ gmx_fio_do_real(fio,ir->dihre_fc);
+ if (file_version < 56) {
+ gmx_fio_do_real(fio,rdum);
+ gmx_fio_do_int(fio,idum);
+ }
+ } else {
+ ir->dihre_fc=0;
+ }
+
+ gmx_fio_do_real(fio,ir->em_stepsize);
+ gmx_fio_do_real(fio,ir->em_tol);
+ if (file_version >= 22)
+ gmx_fio_do_gmx_bool(fio,ir->bShakeSOR);
+ else if (bRead)
+ ir->bShakeSOR = TRUE;
+ if (file_version >= 11)
+ gmx_fio_do_int(fio,ir->niter);
+ else if (bRead) {
+ ir->niter = 25;
+ fprintf(stderr,"Note: niter not in run input file, setting it to %d\n",
+ ir->niter);
+ }
+ if (file_version >= 21)
+ gmx_fio_do_real(fio,ir->fc_stepsize);
+ else
+ ir->fc_stepsize = 0;
+ gmx_fio_do_int(fio,ir->eConstrAlg);
+ gmx_fio_do_int(fio,ir->nProjOrder);
+ gmx_fio_do_real(fio,ir->LincsWarnAngle);
+ if (file_version <= 14)
+ gmx_fio_do_int(fio,idum);
+ if (file_version >=26)
+ gmx_fio_do_int(fio,ir->nLincsIter);
+ else if (bRead) {
+ ir->nLincsIter = 1;
+ fprintf(stderr,"Note: nLincsIter not in run input file, setting it to %d\n",
+ ir->nLincsIter);
+ }
+ if (file_version < 33)
+ gmx_fio_do_real(fio,bd_temp);
+ gmx_fio_do_real(fio,ir->bd_fric);
+ gmx_fio_do_int(fio,ir->ld_seed);
+ if (file_version >= 33) {
+ for(i=0; i<DIM; i++)
+ gmx_fio_do_rvec(fio,ir->deform[i]);
+ } else {
+ for(i=0; i<DIM; i++)
+ clear_rvec(ir->deform[i]);
+ }
+ if (file_version >= 14)
+ gmx_fio_do_real(fio,ir->cos_accel);
+ else if (bRead)
+ ir->cos_accel = 0;
+ gmx_fio_do_int(fio,ir->userint1);
+ gmx_fio_do_int(fio,ir->userint2);
+ gmx_fio_do_int(fio,ir->userint3);
+ gmx_fio_do_int(fio,ir->userint4);
+ gmx_fio_do_real(fio,ir->userreal1);
+ gmx_fio_do_real(fio,ir->userreal2);
+ gmx_fio_do_real(fio,ir->userreal3);
+ gmx_fio_do_real(fio,ir->userreal4);
+
+ /* pull stuff */
+ if (file_version >= 48) {
+ gmx_fio_do_int(fio,ir->ePull);
+ if (ir->ePull != epullNO) {
+ if (bRead)
+ snew(ir->pull,1);
+ do_pull(fio, ir->pull,bRead,file_version);
+ }
+ } else {
+ ir->ePull = epullNO;
+ }
+
+ /* Enforced rotation */
+ if (file_version >= 74) {
+ gmx_fio_do_int(fio,ir->bRot);
+ if (ir->bRot == TRUE) {
+ if (bRead)
+ snew(ir->rot,1);
+ do_rot(fio, ir->rot,bRead,file_version);
+ }
+ } else {
+ ir->bRot = FALSE;
+ }
+
+ /* grpopts stuff */
+ gmx_fio_do_int(fio,ir->opts.ngtc);
+ if (file_version >= 69) {
+ gmx_fio_do_int(fio,ir->opts.nhchainlength);
+ } else {
+ ir->opts.nhchainlength = 1;
+ }
+ gmx_fio_do_int(fio,ir->opts.ngacc);
+ gmx_fio_do_int(fio,ir->opts.ngfrz);
+ gmx_fio_do_int(fio,ir->opts.ngener);
+
+ if (bRead) {
+ snew(ir->opts.nrdf, ir->opts.ngtc);
+ snew(ir->opts.ref_t, ir->opts.ngtc);
+ snew(ir->opts.annealing, ir->opts.ngtc);
+ snew(ir->opts.anneal_npoints, ir->opts.ngtc);
+ snew(ir->opts.anneal_time, ir->opts.ngtc);
+ snew(ir->opts.anneal_temp, ir->opts.ngtc);
+ snew(ir->opts.tau_t, ir->opts.ngtc);
+ snew(ir->opts.nFreeze,ir->opts.ngfrz);
+ snew(ir->opts.acc, ir->opts.ngacc);
+ snew(ir->opts.egp_flags,ir->opts.ngener*ir->opts.ngener);
+ }
+ if (ir->opts.ngtc > 0) {
+ if (bRead && file_version<13) {
+ snew(tmp,ir->opts.ngtc);
+ bDum=gmx_fio_ndo_int(fio,tmp, ir->opts.ngtc);
+ for(i=0; i<ir->opts.ngtc; i++)
+ ir->opts.nrdf[i] = tmp[i];
+ sfree(tmp);
+ } else {
+ bDum=gmx_fio_ndo_real(fio,ir->opts.nrdf, ir->opts.ngtc);
+ }
+ bDum=gmx_fio_ndo_real(fio,ir->opts.ref_t,ir->opts.ngtc);
+ bDum=gmx_fio_ndo_real(fio,ir->opts.tau_t,ir->opts.ngtc);
+ if (file_version<33 && ir->eI==eiBD) {
+ for(i=0; i<ir->opts.ngtc; i++)
+ ir->opts.tau_t[i] = bd_temp;
+ }
+ }
+ if (ir->opts.ngfrz > 0)
+ bDum=gmx_fio_ndo_ivec(fio,ir->opts.nFreeze,ir->opts.ngfrz);
+ if (ir->opts.ngacc > 0)
+ gmx_fio_ndo_rvec(fio,ir->opts.acc,ir->opts.ngacc);
+ if (file_version >= 12)
+ bDum=gmx_fio_ndo_int(fio,ir->opts.egp_flags,
+ ir->opts.ngener*ir->opts.ngener);
+
+ if(bRead && file_version < 26) {
+ for(i=0;i<ir->opts.ngtc;i++) {
+ if(bSimAnn) {
+ ir->opts.annealing[i] = eannSINGLE;
+ ir->opts.anneal_npoints[i] = 2;
+ snew(ir->opts.anneal_time[i],2);
+ snew(ir->opts.anneal_temp[i],2);
+ /* calculate the starting/ending temperatures from reft, zerotemptime, and nsteps */
+ finish_t = ir->init_t + ir->nsteps * ir->delta_t;
+ init_temp = ir->opts.ref_t[i]*(1-ir->init_t/zerotemptime);
+ finish_temp = ir->opts.ref_t[i]*(1-finish_t/zerotemptime);
+ ir->opts.anneal_time[i][0] = ir->init_t;
+ ir->opts.anneal_time[i][1] = finish_t;
+ ir->opts.anneal_temp[i][0] = init_temp;
+ ir->opts.anneal_temp[i][1] = finish_temp;
+ } else {
+ ir->opts.annealing[i] = eannNO;
+ ir->opts.anneal_npoints[i] = 0;
+ }
+ }
+ } else {
+ /* file version 26 or later */
+ /* First read the lists with annealing and npoints for each group */
+ bDum=gmx_fio_ndo_int(fio,ir->opts.annealing,ir->opts.ngtc);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.anneal_npoints,ir->opts.ngtc);
+ for(j=0;j<(ir->opts.ngtc);j++) {
+ k=ir->opts.anneal_npoints[j];
+ if(bRead) {
+ snew(ir->opts.anneal_time[j],k);
+ snew(ir->opts.anneal_temp[j],k);
+ }
+ bDum=gmx_fio_ndo_real(fio,ir->opts.anneal_time[j],k);
+ bDum=gmx_fio_ndo_real(fio,ir->opts.anneal_temp[j],k);
+ }
+ }
+ /* Walls */
+ if (file_version >= 45) {
+ gmx_fio_do_int(fio,ir->nwall);
+ gmx_fio_do_int(fio,ir->wall_type);
+ if (file_version >= 50)
+ gmx_fio_do_real(fio,ir->wall_r_linpot);
+ else
+ ir->wall_r_linpot = -1;
+ gmx_fio_do_int(fio,ir->wall_atomtype[0]);
+ gmx_fio_do_int(fio,ir->wall_atomtype[1]);
+ gmx_fio_do_real(fio,ir->wall_density[0]);
+ gmx_fio_do_real(fio,ir->wall_density[1]);
+ gmx_fio_do_real(fio,ir->wall_ewald_zfac);
+ } else {
+ ir->nwall = 0;
+ ir->wall_type = 0;
+ ir->wall_atomtype[0] = -1;
+ ir->wall_atomtype[1] = -1;
+ ir->wall_density[0] = 0;
+ ir->wall_density[1] = 0;
+ ir->wall_ewald_zfac = 3;
+ }
+ /* Cosine stuff for electric fields */
+ for(j=0; (j<DIM); j++) {
+ gmx_fio_do_int(fio,ir->ex[j].n);
+ gmx_fio_do_int(fio,ir->et[j].n);
+ if (bRead) {
+ snew(ir->ex[j].a, ir->ex[j].n);
+ snew(ir->ex[j].phi,ir->ex[j].n);
+ snew(ir->et[j].a, ir->et[j].n);
+ snew(ir->et[j].phi,ir->et[j].n);
+ }
+ bDum=gmx_fio_ndo_real(fio,ir->ex[j].a, ir->ex[j].n);
+ bDum=gmx_fio_ndo_real(fio,ir->ex[j].phi,ir->ex[j].n);
+ bDum=gmx_fio_ndo_real(fio,ir->et[j].a, ir->et[j].n);
+ bDum=gmx_fio_ndo_real(fio,ir->et[j].phi,ir->et[j].n);
+ }
+
+ /* QMMM stuff */
+ if(file_version>=39){
+ gmx_fio_do_gmx_bool(fio,ir->bQMMM);
+ gmx_fio_do_int(fio,ir->QMMMscheme);
+ gmx_fio_do_real(fio,ir->scalefactor);
+ gmx_fio_do_int(fio,ir->opts.ngQM);
+ if (bRead) {
+ snew(ir->opts.QMmethod, ir->opts.ngQM);
+ snew(ir->opts.QMbasis, ir->opts.ngQM);
+ snew(ir->opts.QMcharge, ir->opts.ngQM);
+ snew(ir->opts.QMmult, ir->opts.ngQM);
+ snew(ir->opts.bSH, ir->opts.ngQM);
+ snew(ir->opts.CASorbitals, ir->opts.ngQM);
+ snew(ir->opts.CASelectrons,ir->opts.ngQM);
+ snew(ir->opts.SAon, ir->opts.ngQM);
+ snew(ir->opts.SAoff, ir->opts.ngQM);
+ snew(ir->opts.SAsteps, ir->opts.ngQM);
+ snew(ir->opts.bOPT, ir->opts.ngQM);
+ snew(ir->opts.bTS, ir->opts.ngQM);
+ }
+ if (ir->opts.ngQM > 0) {
+ bDum=gmx_fio_ndo_int(fio,ir->opts.QMmethod,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.QMbasis,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.QMcharge,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.QMmult,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bSH,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.CASorbitals,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.CASelectrons,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_real(fio,ir->opts.SAon,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_real(fio,ir->opts.SAoff,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_int(fio,ir->opts.SAsteps,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bOPT,ir->opts.ngQM);
+ bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bTS,ir->opts.ngQM);
+ }
+ /* end of QMMM stuff */
+ }
+}
+
+
+static void do_harm(t_fileio *fio, t_iparams *iparams,gmx_bool bRead)
+{
+ gmx_fio_do_real(fio,iparams->harmonic.rA);
+ gmx_fio_do_real(fio,iparams->harmonic.krA);
+ gmx_fio_do_real(fio,iparams->harmonic.rB);
+ gmx_fio_do_real(fio,iparams->harmonic.krB);
+}
+
+void do_iparams(t_fileio *fio, t_functype ftype,t_iparams *iparams,
+ gmx_bool bRead, int file_version)
+{
+ int i;
+ gmx_bool bDum;
+ real rdum;
+
+ if (!bRead)
+ gmx_fio_set_comment(fio, interaction_function[ftype].name);
+ switch (ftype) {
+ case F_ANGLES:
+ case F_G96ANGLES:
+ case F_BONDS:
+ case F_G96BONDS:
+ case F_HARMONIC:
+ case F_IDIHS:
+ do_harm(fio, iparams,bRead);
+ if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && bRead) {
+ /* Correct incorrect storage of parameters */
+ iparams->pdihs.phiB = iparams->pdihs.phiA;
+ iparams->pdihs.cpB = iparams->pdihs.cpA;
+ }
+ break;
+ case F_FENEBONDS:
+ gmx_fio_do_real(fio,iparams->fene.bm);
+ gmx_fio_do_real(fio,iparams->fene.kb);
+ break;
+ case F_RESTRBONDS:
+ gmx_fio_do_real(fio,iparams->restraint.lowA);
+ gmx_fio_do_real(fio,iparams->restraint.up1A);
+ gmx_fio_do_real(fio,iparams->restraint.up2A);
+ gmx_fio_do_real(fio,iparams->restraint.kA);
+ gmx_fio_do_real(fio,iparams->restraint.lowB);
+ gmx_fio_do_real(fio,iparams->restraint.up1B);
+ gmx_fio_do_real(fio,iparams->restraint.up2B);
+ gmx_fio_do_real(fio,iparams->restraint.kB);
+ break;
+ case F_TABBONDS:
+ case F_TABBONDSNC:
+ case F_TABANGLES:
+ case F_TABDIHS:
+ gmx_fio_do_real(fio,iparams->tab.kA);
+ gmx_fio_do_int(fio,iparams->tab.table);
+ gmx_fio_do_real(fio,iparams->tab.kB);
+ break;
+ case F_CROSS_BOND_BONDS:
+ gmx_fio_do_real(fio,iparams->cross_bb.r1e);
+ gmx_fio_do_real(fio,iparams->cross_bb.r2e);
+ gmx_fio_do_real(fio,iparams->cross_bb.krr);
+ break;
+ case F_CROSS_BOND_ANGLES:
+ gmx_fio_do_real(fio,iparams->cross_ba.r1e);
+ gmx_fio_do_real(fio,iparams->cross_ba.r2e);
+ gmx_fio_do_real(fio,iparams->cross_ba.r3e);
+ gmx_fio_do_real(fio,iparams->cross_ba.krt);
+ break;
+ case F_UREY_BRADLEY:
+ gmx_fio_do_real(fio,iparams->u_b.theta);
+ gmx_fio_do_real(fio,iparams->u_b.ktheta);
+ gmx_fio_do_real(fio,iparams->u_b.r13);
+ gmx_fio_do_real(fio,iparams->u_b.kUB);
+ break;
+ case F_QUARTIC_ANGLES:
+ gmx_fio_do_real(fio,iparams->qangle.theta);
+ bDum=gmx_fio_ndo_real(fio,iparams->qangle.c,5);
+ break;
+ case F_BHAM:
+ gmx_fio_do_real(fio,iparams->bham.a);
+ gmx_fio_do_real(fio,iparams->bham.b);
+ gmx_fio_do_real(fio,iparams->bham.c);
+ break;
+ case F_MORSE:
+ gmx_fio_do_real(fio,iparams->morse.b0);
+ gmx_fio_do_real(fio,iparams->morse.cb);
+ gmx_fio_do_real(fio,iparams->morse.beta);
+ break;
+ case F_CUBICBONDS:
+ gmx_fio_do_real(fio,iparams->cubic.b0);
+ gmx_fio_do_real(fio,iparams->cubic.kb);
+ gmx_fio_do_real(fio,iparams->cubic.kcub);
+ break;
+ case F_CONNBONDS:
+ break;
+ case F_POLARIZATION:
+ gmx_fio_do_real(fio,iparams->polarize.alpha);
+ break;
+ case F_WATER_POL:
+ if (file_version < 31)
+ gmx_fatal(FARGS,"Old tpr files with water_polarization not supported. Make a new.");
+ gmx_fio_do_real(fio,iparams->wpol.al_x);
+ gmx_fio_do_real(fio,iparams->wpol.al_y);
+ gmx_fio_do_real(fio,iparams->wpol.al_z);
+ gmx_fio_do_real(fio,iparams->wpol.rOH);
+ gmx_fio_do_real(fio,iparams->wpol.rHH);
+ gmx_fio_do_real(fio,iparams->wpol.rOD);
+ break;
+ case F_THOLE_POL:
+ gmx_fio_do_real(fio,iparams->thole.a);
+ gmx_fio_do_real(fio,iparams->thole.alpha1);
+ gmx_fio_do_real(fio,iparams->thole.alpha2);
+ gmx_fio_do_real(fio,iparams->thole.rfac);
+ break;
+ case F_LJ:
+ gmx_fio_do_real(fio,iparams->lj.c6);
+ gmx_fio_do_real(fio,iparams->lj.c12);
+ break;
+ case F_LJ14:
+ gmx_fio_do_real(fio,iparams->lj14.c6A);
+ gmx_fio_do_real(fio,iparams->lj14.c12A);
+ gmx_fio_do_real(fio,iparams->lj14.c6B);
+ gmx_fio_do_real(fio,iparams->lj14.c12B);
+ break;
+ case F_LJC14_Q:
+ gmx_fio_do_real(fio,iparams->ljc14.fqq);
+ gmx_fio_do_real(fio,iparams->ljc14.qi);
+ gmx_fio_do_real(fio,iparams->ljc14.qj);
+ gmx_fio_do_real(fio,iparams->ljc14.c6);
+ gmx_fio_do_real(fio,iparams->ljc14.c12);
+ break;
+ case F_LJC_PAIRS_NB:
+ gmx_fio_do_real(fio,iparams->ljcnb.qi);
+ gmx_fio_do_real(fio,iparams->ljcnb.qj);
+ gmx_fio_do_real(fio,iparams->ljcnb.c6);
+ gmx_fio_do_real(fio,iparams->ljcnb.c12);
+ break;
+ case F_PDIHS:
+ case F_PIDIHS:
+ case F_ANGRES:
+ case F_ANGRESZ:
+ gmx_fio_do_real(fio,iparams->pdihs.phiA);
+ gmx_fio_do_real(fio,iparams->pdihs.cpA);
+ if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && file_version < 42) {
+ /* Read the incorrectly stored multiplicity */
+ gmx_fio_do_real(fio,iparams->harmonic.rB);
+ gmx_fio_do_real(fio,iparams->harmonic.krB);
+ iparams->pdihs.phiB = iparams->pdihs.phiA;
+ iparams->pdihs.cpB = iparams->pdihs.cpA;
+ } else {
+ gmx_fio_do_real(fio,iparams->pdihs.phiB);
+ gmx_fio_do_real(fio,iparams->pdihs.cpB);
+ gmx_fio_do_int(fio,iparams->pdihs.mult);
+ }
+ break;
+ case F_DISRES:
+ gmx_fio_do_int(fio,iparams->disres.label);
+ gmx_fio_do_int(fio,iparams->disres.type);
+ gmx_fio_do_real(fio,iparams->disres.low);
+ gmx_fio_do_real(fio,iparams->disres.up1);
+ gmx_fio_do_real(fio,iparams->disres.up2);
+ gmx_fio_do_real(fio,iparams->disres.kfac);
+ break;
+ case F_ORIRES:
+ gmx_fio_do_int(fio,iparams->orires.ex);
+ gmx_fio_do_int(fio,iparams->orires.label);
+ gmx_fio_do_int(fio,iparams->orires.power);
+ gmx_fio_do_real(fio,iparams->orires.c);
+ gmx_fio_do_real(fio,iparams->orires.obs);
+ gmx_fio_do_real(fio,iparams->orires.kfac);
+ break;
+ case F_DIHRES:
+ gmx_fio_do_int(fio,iparams->dihres.power);
+ gmx_fio_do_int(fio,iparams->dihres.label);
+ gmx_fio_do_real(fio,iparams->dihres.phi);
+ gmx_fio_do_real(fio,iparams->dihres.dphi);
+ gmx_fio_do_real(fio,iparams->dihres.kfac);
+ break;
+ case F_POSRES:
+ gmx_fio_do_rvec(fio,iparams->posres.pos0A);
+ gmx_fio_do_rvec(fio,iparams->posres.fcA);
+ if (bRead && file_version < 27) {
+ copy_rvec(iparams->posres.pos0A,iparams->posres.pos0B);
+ copy_rvec(iparams->posres.fcA,iparams->posres.fcB);
+ } else {
+ gmx_fio_do_rvec(fio,iparams->posres.pos0B);
+ gmx_fio_do_rvec(fio,iparams->posres.fcB);
+ }
+ break;
+ case F_RBDIHS:
+ bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcA,NR_RBDIHS);
+ if(file_version>=25)
+ bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcB,NR_RBDIHS);
+ break;
+ case F_FOURDIHS:
+ /* Fourier dihedrals are internally represented
+ * as Ryckaert-Bellemans since those are faster to compute.
+ */
+ bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcA, NR_RBDIHS);
+ bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcB, NR_RBDIHS);
+ break;
+ case F_CONSTR:
+ case F_CONSTRNC:
+ gmx_fio_do_real(fio,iparams->constr.dA);
+ gmx_fio_do_real(fio,iparams->constr.dB);
+ break;
+ case F_SETTLE:
+ gmx_fio_do_real(fio,iparams->settle.doh);
+ gmx_fio_do_real(fio,iparams->settle.dhh);
+ break;
+ case F_VSITE2:
+ gmx_fio_do_real(fio,iparams->vsite.a);
+ break;
+ case F_VSITE3:
+ case F_VSITE3FD:
+ case F_VSITE3FAD:
+ gmx_fio_do_real(fio,iparams->vsite.a);
+ gmx_fio_do_real(fio,iparams->vsite.b);
+ break;
+ case F_VSITE3OUT:
+ case F_VSITE4FD:
+ case F_VSITE4FDN:
+ gmx_fio_do_real(fio,iparams->vsite.a);
+ gmx_fio_do_real(fio,iparams->vsite.b);
+ gmx_fio_do_real(fio,iparams->vsite.c);
+ break;
+ case F_VSITEN:
+ gmx_fio_do_int(fio,iparams->vsiten.n);
+ gmx_fio_do_real(fio,iparams->vsiten.a);
+ break;
+ case F_GB12:
+ case F_GB13:
+ case F_GB14:
+ /* We got rid of some parameters in version 68 */
+ if(bRead && file_version<68)
+ {
+ gmx_fio_do_real(fio,rdum);
+ gmx_fio_do_real(fio,rdum);
+ gmx_fio_do_real(fio,rdum);
+ gmx_fio_do_real(fio,rdum);
+ }
+ gmx_fio_do_real(fio,iparams->gb.sar);
+ gmx_fio_do_real(fio,iparams->gb.st);
+ gmx_fio_do_real(fio,iparams->gb.pi);
+ gmx_fio_do_real(fio,iparams->gb.gbr);
+ gmx_fio_do_real(fio,iparams->gb.bmlt);
+ break;
+ case F_CMAP:
+ gmx_fio_do_int(fio,iparams->cmap.cmapA);
+ gmx_fio_do_int(fio,iparams->cmap.cmapB);
+ break;
+ default:
+ gmx_fatal(FARGS,"unknown function type %d (%s) in %s line %d",
+
+ ftype,interaction_function[ftype].name,__FILE__,__LINE__);
+ }
+ if (!bRead)
+ gmx_fio_unset_comment(fio);
+}
+
+static void do_ilist(t_fileio *fio, t_ilist *ilist,gmx_bool bRead,int file_version,
+ int ftype)
+{
+ int i,k,idum;
+ gmx_bool bDum=TRUE;
+
+ if (!bRead) {
+ gmx_fio_set_comment(fio, interaction_function[ftype].name);
+ }
+ if (file_version < 44) {
+ for(i=0; i<MAXNODES; i++)
+ gmx_fio_do_int(fio,idum);
+ }
+ gmx_fio_do_int(fio,ilist->nr);
+ if (bRead)
+ snew(ilist->iatoms,ilist->nr);
+ bDum=gmx_fio_ndo_int(fio,ilist->iatoms,ilist->nr);
+ if (!bRead)
+ gmx_fio_unset_comment(fio);
+}
+
+static void do_ffparams(t_fileio *fio, gmx_ffparams_t *ffparams,
+ gmx_bool bRead, int file_version)
+{
+ int idum,i,j;
+ gmx_bool bDum=TRUE;
+ unsigned int k;
+
+ gmx_fio_do_int(fio,ffparams->atnr);
+ if (file_version < 57) {
+ gmx_fio_do_int(fio,idum);
+ }
+ gmx_fio_do_int(fio,ffparams->ntypes);
+ if (bRead && debug)
+ fprintf(debug,"ffparams->atnr = %d, ntypes = %d\n",
+ ffparams->atnr,ffparams->ntypes);
+ if (bRead) {
+ snew(ffparams->functype,ffparams->ntypes);
+ snew(ffparams->iparams,ffparams->ntypes);
+ }
+ /* Read/write all the function types */
+ bDum=gmx_fio_ndo_int(fio,ffparams->functype,ffparams->ntypes);
+ if (bRead && debug)
+ pr_ivec(debug,0,"functype",ffparams->functype,ffparams->ntypes,TRUE);
+
+ if (file_version >= 66) {
+ gmx_fio_do_double(fio,ffparams->reppow);
+ } else {
+ ffparams->reppow = 12.0;
+ }
+
+ if (file_version >= 57) {
+ gmx_fio_do_real(fio,ffparams->fudgeQQ);
+ }
+
+ /* Check whether all these function types are supported by the code.
+ * In practice the code is backwards compatible, which means that the
+ * numbering may have to be altered from old numbering to new numbering
+ */
+ for (i=0; (i<ffparams->ntypes); i++) {
+ if (bRead)
+ /* Loop over file versions */
+ for (k=0; (k<NFTUPD); k++)
+ /* Compare the read file_version to the update table */
+ if ((file_version < ftupd[k].fvnr) &&
+ (ffparams->functype[i] >= ftupd[k].ftype)) {
+ ffparams->functype[i] += 1;
+ if (debug) {
+ fprintf(debug,"Incrementing function type %d to %d (due to %s)\n",
+ i,ffparams->functype[i],
+ interaction_function[ftupd[k].ftype].longname);
+ fflush(debug);
+ }
+ }
+
+ do_iparams(fio, ffparams->functype[i],&ffparams->iparams[i],bRead,
+ file_version);
+ if (bRead && debug)
+ pr_iparams(debug,ffparams->functype[i],&ffparams->iparams[i]);
+ }
+}
+
+static void do_ilists(t_fileio *fio, t_ilist *ilist,gmx_bool bRead,
+ int file_version)
+{
+ int i,j,renum[F_NRE];
+ gmx_bool bDum=TRUE,bClear;
+ unsigned int k;
+
+ for(j=0; (j<F_NRE); j++) {
+ bClear = FALSE;
+ if (bRead)
+ for (k=0; k<NFTUPD; k++)
+ if ((file_version < ftupd[k].fvnr) && (j == ftupd[k].ftype))
+ bClear = TRUE;
+ if (bClear) {
+ ilist[j].nr = 0;
+ ilist[j].iatoms = NULL;
+ } else {
+ do_ilist(fio, &ilist[j],bRead,file_version,j);
+ }
+ /*
+ if (bRead && gmx_debug_at)
+ pr_ilist(debug,0,interaction_function[j].longname,
+ functype,&ilist[j],TRUE);
+ */
+ }
+}
+
+static void do_idef(t_fileio *fio, gmx_ffparams_t *ffparams,gmx_moltype_t *molt,
+ gmx_bool bRead, int file_version)
+{
+ do_ffparams(fio, ffparams,bRead,file_version);
+
+ if (file_version >= 54) {
+ gmx_fio_do_real(fio,ffparams->fudgeQQ);
+ }
+
+ do_ilists(fio, molt->ilist,bRead,file_version);
+}
+
+static void do_block(t_fileio *fio, t_block *block,gmx_bool bRead,int file_version)
+{
+ int i,idum,dum_nra,*dum_a;
+ gmx_bool bDum=TRUE;
+
+ if (file_version < 44)
+ for(i=0; i<MAXNODES; i++)
+ gmx_fio_do_int(fio,idum);
+ gmx_fio_do_int(fio,block->nr);
+ if (file_version < 51)
+ gmx_fio_do_int(fio,dum_nra);
+ if (bRead) {
+ block->nalloc_index = block->nr+1;
+ snew(block->index,block->nalloc_index);
+ }
+ bDum=gmx_fio_ndo_int(fio,block->index,block->nr+1);
+
+ if (file_version < 51 && dum_nra > 0) {
+ snew(dum_a,dum_nra);
+ bDum=gmx_fio_ndo_int(fio,dum_a,dum_nra);
+ sfree(dum_a);
+ }
+}
+
+static void do_blocka(t_fileio *fio, t_blocka *block,gmx_bool bRead,
+ int file_version)
+{
+ int i,idum;
+ gmx_bool bDum=TRUE;
+
+ if (file_version < 44)
+ for(i=0; i<MAXNODES; i++)
+ gmx_fio_do_int(fio,idum);
+ gmx_fio_do_int(fio,block->nr);
+ gmx_fio_do_int(fio,block->nra);
+ if (bRead) {
+ block->nalloc_index = block->nr+1;
+ snew(block->index,block->nalloc_index);
+ block->nalloc_a = block->nra;
+ snew(block->a,block->nalloc_a);
+ }
+ bDum=gmx_fio_ndo_int(fio,block->index,block->nr+1);
+ bDum=gmx_fio_ndo_int(fio,block->a,block->nra);
+}
+
+static void do_atom(t_fileio *fio, t_atom *atom,int ngrp,gmx_bool bRead,
+ int file_version, gmx_groups_t *groups,int atnr)
+{
+ int i,myngrp;
+
+ gmx_fio_do_real(fio,atom->m);
+ gmx_fio_do_real(fio,atom->q);
+ gmx_fio_do_real(fio,atom->mB);
+ gmx_fio_do_real(fio,atom->qB);
+ gmx_fio_do_ushort(fio, atom->type);
+ gmx_fio_do_ushort(fio, atom->typeB);
+ gmx_fio_do_int(fio,atom->ptype);
+ gmx_fio_do_int(fio,atom->resind);
+ if (file_version >= 52)
+ gmx_fio_do_int(fio,atom->atomnumber);
+ else if (bRead)
+ atom->atomnumber = NOTSET;
+ if (file_version < 23)
+ myngrp = 8;
+ else if (file_version < 39)
+ myngrp = 9;
+ else
+ myngrp = ngrp;
+
+ if (file_version < 57) {
+ unsigned char uchar[egcNR];
+ gmx_fio_ndo_uchar(fio,uchar,myngrp);
+ for(i=myngrp; (i<ngrp); i++) {
+ uchar[i] = 0;
+ }
+ /* Copy the old data format to the groups struct */
+ for(i=0; i<ngrp; i++) {
+ groups->grpnr[i][atnr] = uchar[i];
+ }
+ }
+}
+
+static void do_grps(t_fileio *fio, int ngrp,t_grps grps[],gmx_bool bRead,
+ int file_version)
+{
+ int i,j,myngrp;
+ gmx_bool bDum=TRUE;
+
+ if (file_version < 23)
+ myngrp = 8;
+ else if (file_version < 39)
+ myngrp = 9;
+ else
+ myngrp = ngrp;
+
+ for(j=0; (j<ngrp); j++) {
+ if (j<myngrp) {
+ gmx_fio_do_int(fio,grps[j].nr);
+ if (bRead)
+ snew(grps[j].nm_ind,grps[j].nr);
+ bDum=gmx_fio_ndo_int(fio,grps[j].nm_ind,grps[j].nr);
+ }
+ else {
+ grps[j].nr = 1;
+ snew(grps[j].nm_ind,grps[j].nr);
+ }
+ }
+}
+
+static void do_symstr(t_fileio *fio, char ***nm,gmx_bool bRead,t_symtab *symtab)
+{
+ int ls;
+
+ if (bRead) {
+ gmx_fio_do_int(fio,ls);
+ *nm = get_symtab_handle(symtab,ls);
+ }
+ else {
+ ls = lookup_symtab(symtab,*nm);
+ gmx_fio_do_int(fio,ls);
+ }
+}
+
+static void do_strstr(t_fileio *fio, int nstr,char ***nm,gmx_bool bRead,
+ t_symtab *symtab)
+{
+ int j;
+
+ for (j=0; (j<nstr); j++)
+ do_symstr(fio, &(nm[j]),bRead,symtab);
+}
+
+static void do_resinfo(t_fileio *fio, int n,t_resinfo *ri,gmx_bool bRead,
+ t_symtab *symtab, int file_version)
+{
+ int j;
+
+ for (j=0; (j<n); j++) {
+ do_symstr(fio, &(ri[j].name),bRead,symtab);
+ if (file_version >= 63) {
+ gmx_fio_do_int(fio,ri[j].nr);
+ gmx_fio_do_uchar(fio, ri[j].ic);
+ } else {
+ ri[j].nr = j + 1;
+ ri[j].ic = ' ';
+ }
+ }
+}
+
+static void do_atoms(t_fileio *fio, t_atoms *atoms,gmx_bool bRead,t_symtab *symtab,
+ int file_version,
+ gmx_groups_t *groups)
+{
+ int i;
+
+ gmx_fio_do_int(fio,atoms->nr);
+ gmx_fio_do_int(fio,atoms->nres);
+ if (file_version < 57) {
+ gmx_fio_do_int(fio,groups->ngrpname);
+ for(i=0; i<egcNR; i++) {
+ groups->ngrpnr[i] = atoms->nr;
+ snew(groups->grpnr[i],groups->ngrpnr[i]);
+ }
+ }
+ if (bRead) {
+ snew(atoms->atom,atoms->nr);
+ snew(atoms->atomname,atoms->nr);
+ snew(atoms->atomtype,atoms->nr);
+ snew(atoms->atomtypeB,atoms->nr);
+ snew(atoms->resinfo,atoms->nres);
+ if (file_version < 57) {
+ snew(groups->grpname,groups->ngrpname);
+ }
+ atoms->pdbinfo = NULL;
+ }
+ for(i=0; (i<atoms->nr); i++) {
+ do_atom(fio, &atoms->atom[i],egcNR,bRead, file_version,groups,i);
+ }
+ do_strstr(fio, atoms->nr,atoms->atomname,bRead,symtab);
+ if (bRead && (file_version <= 20)) {
+ for(i=0; i<atoms->nr; i++) {
+ atoms->atomtype[i] = put_symtab(symtab,"?");
+ atoms->atomtypeB[i] = put_symtab(symtab,"?");
+ }
+ } else {
+ do_strstr(fio, atoms->nr,atoms->atomtype,bRead,symtab);
+ do_strstr(fio, atoms->nr,atoms->atomtypeB,bRead,symtab);
+ }
+ do_resinfo(fio, atoms->nres,atoms->resinfo,bRead,symtab,file_version);
+
+ if (file_version < 57) {
+ do_strstr(fio, groups->ngrpname,groups->grpname,bRead,symtab);
+
+ do_grps(fio, egcNR,groups->grps,bRead,file_version);
+ }
+}
+
+static void do_groups(t_fileio *fio, gmx_groups_t *groups,
+ gmx_bool bRead,t_symtab *symtab,
+ int file_version)
+{
+ int g,n,i;
+ gmx_bool bDum=TRUE;
+
+ do_grps(fio, egcNR,groups->grps,bRead,file_version);
+ gmx_fio_do_int(fio,groups->ngrpname);
+ if (bRead) {
+ snew(groups->grpname,groups->ngrpname);
+ }
+ do_strstr(fio, groups->ngrpname,groups->grpname,bRead,symtab);
+ for(g=0; g<egcNR; g++) {
+ gmx_fio_do_int(fio,groups->ngrpnr[g]);
+ if (groups->ngrpnr[g] == 0) {
+ if (bRead) {
+ groups->grpnr[g] = NULL;
+ }
+ } else {
+ if (bRead) {
+ snew(groups->grpnr[g],groups->ngrpnr[g]);
+ }
+ bDum=gmx_fio_ndo_uchar(fio, groups->grpnr[g],groups->ngrpnr[g]);
+ }
+ }
+}
+
+static void do_atomtypes(t_fileio *fio, t_atomtypes *atomtypes,gmx_bool bRead,
+ t_symtab *symtab,int file_version)
+{
+ int i,j;
+ gmx_bool bDum = TRUE;
+
+ if (file_version > 25) {
+ gmx_fio_do_int(fio,atomtypes->nr);
+ j=atomtypes->nr;
+ if (bRead) {
+ snew(atomtypes->radius,j);
+ snew(atomtypes->vol,j);
+ snew(atomtypes->surftens,j);
+ snew(atomtypes->atomnumber,j);
+ snew(atomtypes->gb_radius,j);
+ snew(atomtypes->S_hct,j);
+ }
+ bDum=gmx_fio_ndo_real(fio,atomtypes->radius,j);
+ bDum=gmx_fio_ndo_real(fio,atomtypes->vol,j);
+ bDum=gmx_fio_ndo_real(fio,atomtypes->surftens,j);
+ if(file_version >= 40)
+ {
+ bDum=gmx_fio_ndo_int(fio,atomtypes->atomnumber,j);
+ }
+ if(file_version >= 60)
+ {
+ bDum=gmx_fio_ndo_real(fio,atomtypes->gb_radius,j);
+ bDum=gmx_fio_ndo_real(fio,atomtypes->S_hct,j);
+ }
+ } else {
+ /* File versions prior to 26 cannot do GBSA,
+ * so they dont use this structure
+ */
+ atomtypes->nr = 0;
+ atomtypes->radius = NULL;
+ atomtypes->vol = NULL;
+ atomtypes->surftens = NULL;
+ atomtypes->atomnumber = NULL;
+ atomtypes->gb_radius = NULL;
+ atomtypes->S_hct = NULL;
+ }
+}
+
+static void do_symtab(t_fileio *fio, t_symtab *symtab,gmx_bool bRead)
+{
+ int i,nr;
+ t_symbuf *symbuf;
+ char buf[STRLEN];
+
+ gmx_fio_do_int(fio,symtab->nr);
+ nr = symtab->nr;
+ if (bRead) {
+ snew(symtab->symbuf,1);
+ symbuf = symtab->symbuf;
+ symbuf->bufsize = nr;
+ snew(symbuf->buf,nr);
+ for (i=0; (i<nr); i++) {
+ gmx_fio_do_string(fio,buf);
+ symbuf->buf[i]=strdup(buf);
+ }
+ }
+ else {
+ symbuf = symtab->symbuf;
+ while (symbuf!=NULL) {
+ for (i=0; (i<symbuf->bufsize) && (i<nr); i++)
+ gmx_fio_do_string(fio,symbuf->buf[i]);
+ nr-=i;
+ symbuf=symbuf->next;
+ }
+ if (nr != 0)
+ gmx_fatal(FARGS,"nr of symtab strings left: %d",nr);
+ }
+}
+
+static void do_cmap(t_fileio *fio, gmx_cmap_t *cmap_grid, gmx_bool bRead)
+{
+ int i,j,ngrid,gs,nelem;
+
+ gmx_fio_do_int(fio,cmap_grid->ngrid);
+ gmx_fio_do_int(fio,cmap_grid->grid_spacing);
+
+ ngrid = cmap_grid->ngrid;
+ gs = cmap_grid->grid_spacing;
+ nelem = gs * gs;
+
+ if(bRead)
+ {
+ snew(cmap_grid->cmapdata,ngrid);
+
+ for(i=0;i<cmap_grid->ngrid;i++)
+ {
+ snew(cmap_grid->cmapdata[i].cmap,4*nelem);
+ }
+ }
+
+ for(i=0;i<cmap_grid->ngrid;i++)
+ {
+ for(j=0;j<nelem;j++)
+ {
+ gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4]);
+ gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+1]);
+ gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+2]);
+ gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+3]);
+ }
+ }
+}
+
+
+void tpx_make_chain_identifiers(t_atoms *atoms,t_block *mols)
+{
+ int m,a,a0,a1,r;
+ char c,chainid;
+ int chainnum;
+
+ /* We always assign a new chain number, but save the chain id characters
+ * for larger molecules.
+ */
+#define CHAIN_MIN_ATOMS 15
+
+ chainnum=0;
+ chainid='A';
+ for(m=0; m<mols->nr; m++)
+ {
+ a0=mols->index[m];
+ a1=mols->index[m+1];
+ if ((a1-a0 >= CHAIN_MIN_ATOMS) && (chainid <= 'Z'))
+ {
+ c=chainid;
+ chainid++;
+ }
+ else
+ {
+ c=' ';
+ }
+ for(a=a0; a<a1; a++)
+ {
+ atoms->resinfo[atoms->atom[a].resind].chainnum = chainnum;
+ atoms->resinfo[atoms->atom[a].resind].chainid = c;
+ }
+ chainnum++;
+ }
+
+ /* Blank out the chain id if there was only one chain */
+ if (chainid == 'B')
+ {
+ for(r=0; r<atoms->nres; r++)
+ {
+ atoms->resinfo[r].chainid = ' ';
+ }
+ }
+}
+
+static void do_moltype(t_fileio *fio, gmx_moltype_t *molt,gmx_bool bRead,
+ t_symtab *symtab, int file_version,
+ gmx_groups_t *groups)
+{
+ int i;
+
+ if (file_version >= 57) {
+ do_symstr(fio, &(molt->name),bRead,symtab);
+ }
+
+ do_atoms(fio, &molt->atoms, bRead, symtab, file_version, groups);
+
+ if (bRead && gmx_debug_at) {
+ pr_atoms(debug,0,"atoms",&molt->atoms,TRUE);
+ }
+
+ if (file_version >= 57) {
+ do_ilists(fio, molt->ilist,bRead,file_version);
+
+ do_block(fio, &molt->cgs,bRead,file_version);
+ if (bRead && gmx_debug_at) {
+ pr_block(debug,0,"cgs",&molt->cgs,TRUE);
+ }
+ }
+
+ /* This used to be in the atoms struct */
+ do_blocka(fio, &molt->excls, bRead, file_version);
+}
+
+static void do_molblock(t_fileio *fio, gmx_molblock_t *molb,gmx_bool bRead,
+ int file_version)
+{
+ int i;
+
+ gmx_fio_do_int(fio,molb->type);
+ gmx_fio_do_int(fio,molb->nmol);
+ gmx_fio_do_int(fio,molb->natoms_mol);
+ /* Position restraint coordinates */
+ gmx_fio_do_int(fio,molb->nposres_xA);
+ if (molb->nposres_xA > 0) {
+ if (bRead) {
+ snew(molb->posres_xA,molb->nposres_xA);
+ }
+ gmx_fio_ndo_rvec(fio,molb->posres_xA,molb->nposres_xA);
+ }
+ gmx_fio_do_int(fio,molb->nposres_xB);
+ if (molb->nposres_xB > 0) {
+ if (bRead) {
+ snew(molb->posres_xB,molb->nposres_xB);
+ }
+ gmx_fio_ndo_rvec(fio,molb->posres_xB,molb->nposres_xB);
+ }
+
+}
+
+static t_block mtop_mols(gmx_mtop_t *mtop)
+{
+ int mb,m,a,mol;
+ t_block mols;
+
+ mols.nr = 0;
+ for(mb=0; mb<mtop->nmolblock; mb++) {
+ mols.nr += mtop->molblock[mb].nmol;
+ }
+ mols.nalloc_index = mols.nr + 1;
+ snew(mols.index,mols.nalloc_index);
+
+ a = 0;
+ m = 0;
+ mols.index[m] = a;
+ for(mb=0; mb<mtop->nmolblock; mb++) {
+ for(mol=0; mol<mtop->molblock[mb].nmol; mol++) {
+ a += mtop->molblock[mb].natoms_mol;
+ m++;
+ mols.index[m] = a;
+ }
+ }
+
+ return mols;
+}
+
+static void add_posres_molblock(gmx_mtop_t *mtop)
+{
+ t_ilist *il;
+ int am,i,mol,a;
+ gmx_bool bFE;
+ gmx_molblock_t *molb;
+ t_iparams *ip;
+
+ il = &mtop->moltype[0].ilist[F_POSRES];
+ if (il->nr == 0) {
+ return;
+ }
+ am = 0;
+ bFE = FALSE;
+ for(i=0; i<il->nr; i+=2) {
+ ip = &mtop->ffparams.iparams[il->iatoms[i]];
+ am = max(am,il->iatoms[i+1]);
+ if (ip->posres.pos0B[XX] != ip->posres.pos0A[XX] ||
+ ip->posres.pos0B[YY] != ip->posres.pos0A[YY] ||
+ ip->posres.pos0B[ZZ] != ip->posres.pos0A[ZZ]) {
+ bFE = TRUE;
+ }
+ }
+ /* Make the posres coordinate block end at a molecule end */
+ mol = 0;
+ while(am >= mtop->mols.index[mol+1]) {
+ mol++;
+ }
+ molb = &mtop->molblock[0];
+ molb->nposres_xA = mtop->mols.index[mol+1];
+ snew(molb->posres_xA,molb->nposres_xA);
+ if (bFE) {
+ molb->nposres_xB = molb->nposres_xA;
+ snew(molb->posres_xB,molb->nposres_xB);
+ } else {
+ molb->nposres_xB = 0;
+ }
+ for(i=0; i<il->nr; i+=2) {
+ ip = &mtop->ffparams.iparams[il->iatoms[i]];
+ a = il->iatoms[i+1];
+ molb->posres_xA[a][XX] = ip->posres.pos0A[XX];
+ molb->posres_xA[a][YY] = ip->posres.pos0A[YY];
+ molb->posres_xA[a][ZZ] = ip->posres.pos0A[ZZ];
+ if (bFE) {
+ molb->posres_xB[a][XX] = ip->posres.pos0B[XX];
+ molb->posres_xB[a][YY] = ip->posres.pos0B[YY];
+ molb->posres_xB[a][ZZ] = ip->posres.pos0B[ZZ];
+ }
+ }
+}
+
+static void set_disres_npair(gmx_mtop_t *mtop)
+{
+ int mt,i,npair;
+ t_iparams *ip;
+ t_ilist *il;
+ t_iatom *a;
+
+ ip = mtop->ffparams.iparams;
+
+ for(mt=0; mt<mtop->nmoltype; mt++) {
+ il = &mtop->moltype[mt].ilist[F_DISRES];
+ if (il->nr > 0) {
+ a = il->iatoms;
+ npair = 0;
+ for(i=0; i<il->nr; i+=3) {
+ npair++;
+ if (i+3 == il->nr || ip[a[i]].disres.label != ip[a[i+3]].disres.label) {
+ ip[a[i]].disres.npair = npair;
+ npair = 0;
+ }
+ }
+ }
+ }
+}
+
+static void do_mtop(t_fileio *fio, gmx_mtop_t *mtop,gmx_bool bRead,
+ int file_version)
+{
+ int mt,mb,i;
+ t_blocka dumb;
+
+ if (bRead)
+ init_mtop(mtop);
+ do_symtab(fio, &(mtop->symtab),bRead);
+ if (bRead && debug)
+ pr_symtab(debug,0,"symtab",&mtop->symtab);
+
+ do_symstr(fio, &(mtop->name),bRead,&(mtop->symtab));
+
+ if (file_version >= 57) {
+ do_ffparams(fio, &mtop->ffparams,bRead,file_version);
+
+ gmx_fio_do_int(fio,mtop->nmoltype);
+ } else {
+ mtop->nmoltype = 1;
+ }
+ if (bRead) {
+ snew(mtop->moltype,mtop->nmoltype);
+ if (file_version < 57) {
+ mtop->moltype[0].name = mtop->name;
+ }
+ }
+ for(mt=0; mt<mtop->nmoltype; mt++) {
+ do_moltype(fio, &mtop->moltype[mt],bRead,&mtop->symtab,file_version,
+ &mtop->groups);
+ }
+
+ if (file_version >= 57) {
+ gmx_fio_do_int(fio,mtop->nmolblock);
+ } else {
+ mtop->nmolblock = 1;
+ }
+ if (bRead) {
+ snew(mtop->molblock,mtop->nmolblock);
+ }
+ if (file_version >= 57) {
+ for(mb=0; mb<mtop->nmolblock; mb++) {
+ do_molblock(fio, &mtop->molblock[mb],bRead,file_version);
+ }
+ gmx_fio_do_int(fio,mtop->natoms);
+ } else {
+ mtop->molblock[0].type = 0;
+ mtop->molblock[0].nmol = 1;
+ mtop->molblock[0].natoms_mol = mtop->moltype[0].atoms.nr;
+ mtop->molblock[0].nposres_xA = 0;
+ mtop->molblock[0].nposres_xB = 0;
+ }
+
+ do_atomtypes (fio, &(mtop->atomtypes),bRead,&(mtop->symtab), file_version);
+ if (bRead && debug)
+ pr_atomtypes(debug,0,"atomtypes",&mtop->atomtypes,TRUE);
+
+ if (file_version < 57) {
+ /* Debug statements are inside do_idef */
+ do_idef (fio, &mtop->ffparams,&mtop->moltype[0],bRead,file_version);
+ mtop->natoms = mtop->moltype[0].atoms.nr;
+ }
+
+ if(file_version >= 65)
+ {
+ do_cmap(fio, &mtop->ffparams.cmap_grid,bRead);
+ }
+ else
+ {
+ mtop->ffparams.cmap_grid.ngrid = 0;
+ mtop->ffparams.cmap_grid.grid_spacing = 0.1;
+ mtop->ffparams.cmap_grid.cmapdata = NULL;
+ }
+
+ if (file_version >= 57) {
+ do_groups(fio, &mtop->groups,bRead,&(mtop->symtab),file_version);
+ }
+
+ if (file_version < 57) {
+ do_block(fio, &mtop->moltype[0].cgs,bRead,file_version);
+ if (bRead && gmx_debug_at) {
+ pr_block(debug,0,"cgs",&mtop->moltype[0].cgs,TRUE);
+ }
+ do_block(fio, &mtop->mols,bRead,file_version);
+ /* Add the posres coordinates to the molblock */
+ add_posres_molblock(mtop);
+ }
+ if (bRead) {
+ if (file_version >= 57) {
+ mtop->mols = mtop_mols(mtop);
+ }
+ if (gmx_debug_at) {
+ pr_block(debug,0,"mols",&mtop->mols,TRUE);
+ }
+ }
+
+ if (file_version < 51) {
+ /* Here used to be the shake blocks */
+ do_blocka(fio, &dumb,bRead,file_version);
+ if (dumb.nr > 0)
+ sfree(dumb.index);
+ if (dumb.nra > 0)
+ sfree(dumb.a);
+ }
+
+ if (bRead) {
+ close_symtab(&(mtop->symtab));
+ }
+}
+
+/* If TopOnlyOK is TRUE then we can read even future versions
+ * of tpx files, provided the file_generation hasn't changed.
+ * If it is FALSE, we need the inputrecord too, and bail out
+ * if the file is newer than the program.
+ *
+ * The version and generation if the topology (see top of this file)
+ * are returned in the two last arguments.
+ *
+ * If possible, we will read the inputrec even when TopOnlyOK is TRUE.
+ */
+static void do_tpxheader(t_fileio *fio,gmx_bool bRead,t_tpxheader *tpx,
+ gmx_bool TopOnlyOK, int *file_version,
+ int *file_generation)
+{
+ char buf[STRLEN];
+ gmx_bool bDouble;
+ int precision;
+ int fver,fgen;
+ int idum=0;
+ real rdum=0;
+
+ gmx_fio_checktype(fio);
+ gmx_fio_setdebug(fio,bDebugMode());
+
+ /* NEW! XDR tpb file */
+ precision = sizeof(real);
+ if (bRead) {
+ gmx_fio_do_string(fio,buf);
+ if (strncmp(buf,"VERSION",7))
+ gmx_fatal(FARGS,"Can not read file %s,\n"
+ " this file is from a Gromacs version which is older than 2.0\n"
+ " Make a new one with grompp or use a gro or pdb file, if possible",
+ gmx_fio_getname(fio));
+ gmx_fio_do_int(fio,precision);
+ bDouble = (precision == sizeof(double));
+ if ((precision != sizeof(float)) && !bDouble)
+ gmx_fatal(FARGS,"Unknown precision in file %s: real is %d bytes "
+ "instead of %d or %d",
+ gmx_fio_getname(fio),precision,sizeof(float),sizeof(double));
+ gmx_fio_setprecision(fio,bDouble);
+ fprintf(stderr,"Reading file %s, %s (%s precision)\n",
+ gmx_fio_getname(fio),buf,bDouble ? "double" : "single");
+ }
+ else {
+ gmx_fio_write_string(fio,GromacsVersion());
+ bDouble = (precision == sizeof(double));
+ gmx_fio_setprecision(fio,bDouble);
+ gmx_fio_do_int(fio,precision);
+ fver = tpx_version;
+ fgen = tpx_generation;
+ }
+
+ /* Check versions! */
+ gmx_fio_do_int(fio,fver);
+
+ if(fver>=26)
+ gmx_fio_do_int(fio,fgen);
+ else
+ fgen=0;
+
+ if(file_version!=NULL)
+ *file_version = fver;
+ if(file_generation!=NULL)
+ *file_generation = fgen;
+
+
+ if ((fver <= tpx_incompatible_version) ||
+ ((fver > tpx_version) && !TopOnlyOK) ||
+ (fgen > tpx_generation))
+ gmx_fatal(FARGS,"reading tpx file (%s) version %d with version %d program",
+ gmx_fio_getname(fio),fver,tpx_version);
+
+ do_section(fio,eitemHEADER,bRead);
+ gmx_fio_do_int(fio,tpx->natoms);
+ if (fver >= 28)
+ gmx_fio_do_int(fio,tpx->ngtc);
+ else
+ tpx->ngtc = 0;
+ if (fver < 62) {
+ gmx_fio_do_int(fio,idum);
+ gmx_fio_do_real(fio,rdum);
+ }
+ gmx_fio_do_real(fio,tpx->lambda);
+ gmx_fio_do_int(fio,tpx->bIr);
+ gmx_fio_do_int(fio,tpx->bTop);
+ gmx_fio_do_int(fio,tpx->bX);
+ gmx_fio_do_int(fio,tpx->bV);
+ gmx_fio_do_int(fio,tpx->bF);
+ gmx_fio_do_int(fio,tpx->bBox);
+
+ if((fgen > tpx_generation)) {
+ /* This can only happen if TopOnlyOK=TRUE */
+ tpx->bIr=FALSE;
+ }
+}
+
+static int do_tpx(t_fileio *fio, gmx_bool bRead,
+ t_inputrec *ir,t_state *state,rvec *f,gmx_mtop_t *mtop,
+ gmx_bool bXVallocated)
+{
+ t_tpxheader tpx;
+ t_inputrec dum_ir;
+ gmx_mtop_t dum_top;
+ gmx_bool TopOnlyOK,bDum=TRUE;
+ int file_version,file_generation;
+ int i;
+ rvec *xptr,*vptr;
+ int ePBC;
+ gmx_bool bPeriodicMols;
+
+ if (!bRead) {
+ tpx.natoms = state->natoms;
+ tpx.ngtc = state->ngtc;
+ tpx.lambda = state->lambda;
+ tpx.bIr = (ir != NULL);
+ tpx.bTop = (mtop != NULL);
+ tpx.bX = (state->x != NULL);
+ tpx.bV = (state->v != NULL);
+ tpx.bF = (f != NULL);
+ tpx.bBox = TRUE;
+ }
+
+ TopOnlyOK = (ir==NULL);
+
+ do_tpxheader(fio,bRead,&tpx,TopOnlyOK,&file_version,&file_generation);
+
+ if (bRead) {
+ state->flags = 0;
+ state->lambda = tpx.lambda;
+ /* The init_state calls initialize the Nose-Hoover xi integrals to zero */
+ if (bXVallocated) {
+ xptr = state->x;
+ vptr = state->v;
+ init_state(state,0,tpx.ngtc,0,0); /* nose-hoover chains */ /* eventually, need to add nnhpres here? */
+ state->natoms = tpx.natoms;
+ state->nalloc = tpx.natoms;
+ state->x = xptr;
+ state->v = vptr;
+ } else {
+ init_state(state,tpx.natoms,tpx.ngtc,0,0); /* nose-hoover chains */
+ }
+ }
+
+#define do_test(fio,b,p) if (bRead && (p!=NULL) && !b) gmx_fatal(FARGS,"No %s in %s",#p,gmx_fio_getname(fio))
+
+ do_test(fio,tpx.bBox,state->box);
+ do_section(fio,eitemBOX,bRead);
+ if (tpx.bBox) {
+ gmx_fio_ndo_rvec(fio,state->box,DIM);
+ if (file_version >= 51) {
+ gmx_fio_ndo_rvec(fio,state->box_rel,DIM);
+ } else {
+ /* We initialize box_rel after reading the inputrec */
+ clear_mat(state->box_rel);
+ }
+ if (file_version >= 28) {
+ gmx_fio_ndo_rvec(fio,state->boxv,DIM);
+ if (file_version < 56) {
+ matrix mdum;
+ gmx_fio_ndo_rvec(fio,mdum,DIM);
+ }
+ }
+ }
+
+ if (state->ngtc > 0 && file_version >= 28) {
+ real *dumv;
+ /*ndo_double(state->nosehoover_xi,state->ngtc,bDum);*/
+ /*ndo_double(state->nosehoover_vxi,state->ngtc,bDum);*/
+ /*ndo_double(state->therm_integral,state->ngtc,bDum);*/
+ snew(dumv,state->ngtc);
+ if (file_version < 69) {
+ bDum=gmx_fio_ndo_real(fio,dumv,state->ngtc);
+ }
+ /* These used to be the Berendsen tcoupl_lambda's */
+ bDum=gmx_fio_ndo_real(fio,dumv,state->ngtc);
+ sfree(dumv);
+ }
+
+ /* Prior to tpx version 26, the inputrec was here.
+ * I moved it to enable partial forward-compatibility
+ * for analysis/viewer programs.
+ */
+ if(file_version<26) {
+ do_test(fio,tpx.bIr,ir);
+ do_section(fio,eitemIR,bRead);
+ if (tpx.bIr) {
+ if (ir) {
+ do_inputrec(fio, ir,bRead,file_version,
+ mtop ? &mtop->ffparams.fudgeQQ : NULL);
+ if (bRead && debug)
+ pr_inputrec(debug,0,"inputrec",ir,FALSE);
+ }
+ else {
+ do_inputrec(fio, &dum_ir,bRead,file_version,
+ mtop ? &mtop->ffparams.fudgeQQ :NULL);
+ if (bRead && debug)
+ pr_inputrec(debug,0,"inputrec",&dum_ir,FALSE);
+ done_inputrec(&dum_ir);
+ }
+
+ }
+ }
+
+ do_test(fio,tpx.bTop,mtop);
+ do_section(fio,eitemTOP,bRead);
+ if (tpx.bTop) {
+ if (mtop) {
+ do_mtop(fio,mtop,bRead, file_version);
+ } else {
+ do_mtop(fio,&dum_top,bRead,file_version);
+ done_mtop(&dum_top,TRUE);
+ }
+ }
+ do_test(fio,tpx.bX,state->x);
+ do_section(fio,eitemX,bRead);
+ if (tpx.bX) {
+ if (bRead) {
+ state->flags |= (1<<estX);
+ }
+ gmx_fio_ndo_rvec(fio,state->x,state->natoms);
+ }
+
+ do_test(fio,tpx.bV,state->v);
+ do_section(fio,eitemV,bRead);
+ if (tpx.bV) {
+ if (bRead) {
+ state->flags |= (1<<estV);
+ }
+ gmx_fio_ndo_rvec(fio,state->v,state->natoms);
+ }
+
+ do_test(fio,tpx.bF,f);
+ do_section(fio,eitemF,bRead);
+ if (tpx.bF) gmx_fio_ndo_rvec(fio,f,state->natoms);
+
+ /* Starting with tpx version 26, we have the inputrec
+ * at the end of the file, so we can ignore it
+ * if the file is never than the software (but still the
+ * same generation - see comments at the top of this file.
+ *
+ *
+ */
+ ePBC = -1;
+ bPeriodicMols = FALSE;
+ if (file_version >= 26) {
+ do_test(fio,tpx.bIr,ir);
+ do_section(fio,eitemIR,bRead);
+ if (tpx.bIr) {
+ if (file_version >= 53) {
+ /* Removed the pbc info from do_inputrec, since we always want it */
+ if (!bRead) {
+ ePBC = ir->ePBC;
+ bPeriodicMols = ir->bPeriodicMols;
+ }
+ gmx_fio_do_int(fio,ePBC);
+ gmx_fio_do_gmx_bool(fio,bPeriodicMols);
+ }
+ if (file_generation <= tpx_generation && ir) {
+ do_inputrec(fio, ir,bRead,file_version,mtop ? &mtop->ffparams.fudgeQQ : NULL);
+ if (bRead && debug)
+ pr_inputrec(debug,0,"inputrec",ir,FALSE);
+ if (file_version < 51)
+ set_box_rel(ir,state);
+ if (file_version < 53) {
+ ePBC = ir->ePBC;
+ bPeriodicMols = ir->bPeriodicMols;
+ }
+ }
+ if (bRead && ir && file_version >= 53) {
+ /* We need to do this after do_inputrec, since that initializes ir */
+ ir->ePBC = ePBC;
+ ir->bPeriodicMols = bPeriodicMols;
+ }
+ }
+ }
+
+ if (bRead)
+ {
+ if (tpx.bIr && ir)
+ {
+ if (state->ngtc == 0)
+ {
+ /* Reading old version without tcoupl state data: set it */
+ init_gtc_state(state,ir->opts.ngtc,0,ir->opts.nhchainlength);
+ }
+ if (tpx.bTop && mtop)
+ {
+ if (file_version < 57)
+ {
+ if (mtop->moltype[0].ilist[F_DISRES].nr > 0)
+ {
+ ir->eDisre = edrSimple;
+ }
+ else
+ {
+ ir->eDisre = edrNone;
+ }
+ }
+ set_disres_npair(mtop);
+ }
+ }
+
+ if (tpx.bTop && mtop)
+ {
+ gmx_mtop_finalize(mtop);
+ }
+
+ if (file_version >= 57)
+ {
+ char *env;
+ int ienv;
+ env = getenv("GMX_NOCHARGEGROUPS");
+ if (env != NULL)
+ {
+ sscanf(env,"%d",&ienv);
+ fprintf(stderr,"\nFound env.var. GMX_NOCHARGEGROUPS = %d\n",
+ ienv);
+ if (ienv > 0)
+ {
+ fprintf(stderr,
+ "Will make single atomic charge groups in non-solvent%s\n",
+ ienv > 1 ? " and solvent" : "");
+ gmx_mtop_make_atomic_charge_groups(mtop,ienv==1);
+ }
+ fprintf(stderr,"\n");
+ }
+ }
+ }
+
+ return ePBC;
+}
+
+/************************************************************
+ *
+ * The following routines are the exported ones
+ *
+ ************************************************************/
+
+t_fileio *open_tpx(const char *fn,const char *mode)
+{
+ return gmx_fio_open(fn,mode);
+}
+
+void close_tpx(t_fileio *fio)
+{
+ gmx_fio_close(fio);
+}
+
+void read_tpxheader(const char *fn, t_tpxheader *tpx, gmx_bool TopOnlyOK,
+ int *file_version, int *file_generation)
+{
+ t_fileio *fio;
+
+ fio = open_tpx(fn,"r");
+ do_tpxheader(fio,TRUE,tpx,TopOnlyOK,file_version,file_generation);
+ close_tpx(fio);
+}
+
+void write_tpx_state(const char *fn,
+ t_inputrec *ir,t_state *state,gmx_mtop_t *mtop)
+{
+ t_fileio *fio;
+
+ fio = open_tpx(fn,"w");
+ do_tpx(fio,FALSE,ir,state,NULL,mtop,FALSE);
+ close_tpx(fio);
+}
+
+void read_tpx_state(const char *fn,
+ t_inputrec *ir,t_state *state,rvec *f,gmx_mtop_t *mtop)
+{
+ t_fileio *fio;
+
+ fio = open_tpx(fn,"r");
+ do_tpx(fio,TRUE,ir,state,f,mtop,FALSE);
+ close_tpx(fio);
+}
+
+int read_tpx(const char *fn,
+ t_inputrec *ir, matrix box,int *natoms,
+ rvec *x,rvec *v,rvec *f,gmx_mtop_t *mtop)
+{
+ t_fileio *fio;
+ t_state state;
+ int ePBC;
+
+ state.x = x;
+ state.v = v;
+ fio = open_tpx(fn,"r");
+ ePBC = do_tpx(fio,TRUE,ir,&state,f,mtop,TRUE);
+ close_tpx(fio);
+ *natoms = state.natoms;
+ if (box)
+ copy_mat(state.box,box);
+ state.x = NULL;
+ state.v = NULL;
+ done_state(&state);
+
+ return ePBC;
+}
+
+int read_tpx_top(const char *fn,
+ t_inputrec *ir, matrix box,int *natoms,
+ rvec *x,rvec *v,rvec *f,t_topology *top)
+{
+ gmx_mtop_t mtop;
+ t_topology *ltop;
+ int ePBC;
+
+ ePBC = read_tpx(fn,ir,box,natoms,x,v,f,&mtop);
+
+ *top = gmx_mtop_t_to_t_topology(&mtop);
+
+ return ePBC;
+}
+
+gmx_bool fn2bTPX(const char *file)
+{
+ switch (fn2ftp(file)) {
+ case efTPR:
+ case efTPB:
+ case efTPA:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+gmx_bool read_tps_conf(const char *infile,char *title,t_topology *top,int *ePBC,
+ rvec **x,rvec **v,matrix box,gmx_bool bMass)
+{
+ t_tpxheader header;
+ int natoms,i,version,generation;
+ gmx_bool bTop,bXNULL;
+ gmx_mtop_t *mtop;
+ t_topology *topconv;
+ gmx_atomprop_t aps;
+
+ bTop = fn2bTPX(infile);
+ *ePBC = -1;
+ if (bTop) {
+ read_tpxheader(infile,&header,TRUE,&version,&generation);
+ if (x)
+ snew(*x,header.natoms);
+ if (v)
+ snew(*v,header.natoms);
+ snew(mtop,1);
+ *ePBC = read_tpx(infile,NULL,box,&natoms,
+ (x==NULL) ? NULL : *x,(v==NULL) ? NULL : *v,NULL,mtop);
+ *top = gmx_mtop_t_to_t_topology(mtop);
+ sfree(mtop);
+ strcpy(title,*top->name);
+ tpx_make_chain_identifiers(&top->atoms,&top->mols);
+ }
+ else {
+ get_stx_coordnum(infile,&natoms);
+ init_t_atoms(&top->atoms,natoms,FALSE);
+ bXNULL = (x == NULL);
+ snew(*x,natoms);
+ if (v)
+ snew(*v,natoms);
+ read_stx_conf(infile,title,&top->atoms,*x,(v==NULL) ? NULL : *v,ePBC,box);
+ if (bXNULL) {
+ sfree(*x);
+ x = NULL;
+ }
+ if (bMass) {
+ aps = gmx_atomprop_init();
+ for(i=0; (i<natoms); i++)
+ if (!gmx_atomprop_query(aps,epropMass,
+ *top->atoms.resinfo[top->atoms.atom[i].resind].name,
+ *top->atoms.atomname[i],
+ &(top->atoms.atom[i].m))) {
+ if (debug)
+ fprintf(debug,"Can not find mass for atom %s %d %s, setting to 1\n",
+ *top->atoms.resinfo[top->atoms.atom[i].resind].name,
+ top->atoms.resinfo[top->atoms.atom[i].resind].nr,
+ *top->atoms.atomname[i]);
+ }
+ gmx_atomprop_destroy(aps);
+ }
+ top->idef.ntypes=-1;
+ }
+
+ return bTop;
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This file is completely threadsafe - please keep it that way! */
+#ifdef GMX_THREADS
+#include <thread_mpi.h>
+#endif
+
+
+#include <stdio.h>
+#include "smalloc.h"
+#include "typedefs.h"
+#include "names.h"
+#include "txtdump.h"
+#include "string2.h"
+#include "vec.h"
+
+
+int pr_indent(FILE *fp,int n)
+{
+ int i;
+
+ for (i=0; i<n; i++) (void) fprintf(fp," ");
+ return n;
+}
+
+int available(FILE *fp,void *p,int indent,const char *title)
+{
+ if (!p) {
+ if (indent > 0)
+ pr_indent(fp,indent);
+ (void) fprintf(fp,"%s: not available\n",title);
+ }
+ return (p!=NULL);
+}
+
+int pr_title(FILE *fp,int indent,const char *title)
+{
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s:\n",title);
+ return (indent+INDENT);
+}
+
+int pr_title_n(FILE *fp,int indent,const char *title,int n)
+{
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s (%d):\n",title,n);
+ return (indent+INDENT);
+}
+
+int pr_title_nxn(FILE *fp,int indent,const char *title,int n1,int n2)
+{
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s (%dx%d):\n",title,n1,n2);
+ return (indent+INDENT);
+}
+
+void pr_ivec(FILE *fp,int indent,const char *title,int vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]=%d\n",title,bShowNumbers?i:-1,vec[i]);
+ }
+ }
+}
+
+void pr_ivec_block(FILE *fp,int indent,const char *title,int vec[],int n, gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ i = 0;
+ while (i < n)
+ {
+ j = i+1;
+ while (j < n && vec[j] == vec[j-1]+1)
+ {
+ j++;
+ }
+ /* Print consecutive groups of 3 or more as blocks */
+ if (j - i < 3)
+ {
+ while(i < j)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]=%d\n",
+ title,bShowNumbers?i:-1,vec[i]);
+ i++;
+ }
+ }
+ else
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d,...,%d] = {%d,...,%d}\n",
+ title,
+ bShowNumbers?i:-1,
+ bShowNumbers?j-1:-1,
+ vec[i],vec[j-1]);
+ i = j;
+ }
+ }
+ }
+}
+
+void pr_bvec(FILE *fp,int indent,const char *title,gmx_bool vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]=%s\n",title,bShowNumbers?i:-1,
+ BOOL(vec[i]));
+ }
+ }
+}
+
+void pr_ivecs(FILE *fp,int indent,const char *title,ivec vec[],int n, gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_nxn(fp,indent,title,n,DIM);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={",title,bShowNumbers?i:-1);
+ for (j=0; j<DIM; j++)
+ {
+ if (j!=0) (void) fprintf(fp,", ");
+ fprintf(fp,"%d",vec[i][j]);
+ }
+ (void) fprintf(fp,"}\n");
+ }
+ }
+}
+
+void pr_rvec(FILE *fp,int indent,const char *title,real vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ pr_indent(fp,indent);
+ fprintf(fp,"%s[%d]=%12.5e\n",title,bShowNumbers?i:-1,vec[i]);
+ }
+ }
+}
+
+void pr_dvec(FILE *fp,int indent,const char *title,double vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ pr_indent(fp,indent);
+ fprintf(fp,"%s[%d]=%12.5e\n",title,bShowNumbers?i:-1,vec[i]);
+ }
+ }
+}
+
+
+/*
+void pr_mat(FILE *fp,int indent,char *title,matrix m)
+{
+ int i,j;
+
+ if (available(fp,m,indent,title)) {
+ indent=pr_title_n(fp,indent,title,n);
+ for(i=0; i<n; i++) {
+ pr_indent(fp,indent);
+ fprintf(fp,"%s[%d]=%12.5e %12.5e %12.5e\n",
+ title,bShowNumbers?i:-1,m[i][XX],m[i][YY],m[i][ZZ]);
+ }
+ }
+}
+*/
+
+void pr_rvecs_len(FILE *fp,int indent,const char *title,rvec vec[],int n)
+{
+ int i,j;
+
+ if (available(fp,vec,indent,title)) {
+ indent=pr_title_nxn(fp,indent,title,n,DIM);
+ for (i=0; i<n; i++) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%5d]={",title,i);
+ for (j=0; j<DIM; j++) {
+ if (j != 0)
+ (void) fprintf(fp,", ");
+ (void) fprintf(fp,"%12.5e",vec[i][j]);
+ }
+ (void) fprintf(fp,"} len=%12.5e\n",norm(vec[i]));
+ }
+ }
+}
+
+void pr_rvecs(FILE *fp,int indent,const char *title,rvec vec[],int n)
+{
+ const char *fshort = "%12.5e";
+ const char *flong = "%15.8e";
+ const char *format;
+ int i,j;
+
+ if (getenv("LONGFORMAT") != NULL)
+ format = flong;
+ else
+ format = fshort;
+
+ if (available(fp,vec,indent,title)) {
+ indent=pr_title_nxn(fp,indent,title,n,DIM);
+ for (i=0; i<n; i++) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%5d]={",title,i);
+ for (j=0; j<DIM; j++) {
+ if (j != 0)
+ (void) fprintf(fp,", ");
+ (void) fprintf(fp,format,vec[i][j]);
+ }
+ (void) fprintf(fp,"}\n");
+ }
+ }
+}
+
+
+void pr_reals(FILE *fp,int indent,const char *title,real *vec,int n)
+{
+ int i;
+
+ if (available(fp,vec,indent,title)) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s:\t",title);
+ for(i=0; i<n; i++)
+ fprintf(fp," %10g",vec[i]);
+ (void) fprintf(fp,"\n");
+ }
+}
+
+void pr_doubles(FILE *fp,int indent,const char *title,double *vec,int n)
+{
+ int i;
+
+ if (available(fp,vec,indent,title)) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s:\t",title);
+ for(i=0; i<n; i++)
+ fprintf(fp," %10g",vec[i]);
+ (void) fprintf(fp,"\n");
+ }
+}
+
+static void pr_int(FILE *fp,int indent,const char *title,int i)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %d\n",title,i);
+}
+
+static void pr_gmx_large_int(FILE *fp,int indent,const char *title,gmx_large_int_t i)
+{
+ char buf[STEPSTRSIZE];
+
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %s\n",title,gmx_step_str(i,buf));
+}
+
+static void pr_real(FILE *fp,int indent,const char *title,real r)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %g\n",title,r);
+}
+
+static void pr_double(FILE *fp,int indent,const char *title,double d)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %g\n",title,d);
+}
+
+static void pr_str(FILE *fp,int indent,const char *title,const char *s)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %s\n",title,s);
+}
+
+void pr_qm_opts(FILE *fp,int indent,const char *title,t_grpopts *opts)
+{
+ int i,m,j;
+
+ fprintf(fp,"%s:\n",title);
+
+ pr_int(fp,indent,"ngQM",opts->ngQM);
+ if (opts->ngQM > 0) {
+ pr_ivec(fp,indent,"QMmethod",opts->QMmethod,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"QMbasis",opts->QMbasis,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"QMcharge",opts->QMcharge,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"QMmult",opts->QMmult,opts->ngQM,FALSE);
+ pr_bvec(fp,indent,"bSH",opts->bSH,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"CASorbitals",opts->CASorbitals,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"CASelectrons",opts->CASelectrons,opts->ngQM,FALSE);
+ pr_rvec(fp,indent,"SAon",opts->SAon,opts->ngQM,FALSE);
+ pr_rvec(fp,indent,"SAon",opts->SAon,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"SAsteps",opts->SAsteps,opts->ngQM,FALSE);
+ pr_bvec(fp,indent,"bOPT",opts->bOPT,opts->ngQM,FALSE);
+ pr_bvec(fp,indent,"bTS",opts->bTS,opts->ngQM,FALSE);
+ }
+}
+
+static void pr_grp_opts(FILE *out,int indent,const char *title,t_grpopts *opts,
+ gmx_bool bMDPformat)
+{
+ int i,m,j;
+
+ if (!bMDPformat)
+ fprintf(out,"%s:\n",title);
+
+ pr_indent(out,indent);
+ fprintf(out,"nrdf%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10g",opts->nrdf[i]);
+ fprintf(out,"\n");
+
+ pr_indent(out,indent);
+ fprintf(out,"ref_t%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10g",opts->ref_t[i]);
+ fprintf(out,"\n");
+
+ pr_indent(out,indent);
+ fprintf(out,"tau_t%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10g",opts->tau_t[i]);
+ fprintf(out,"\n");
+
+ /* Pretty-print the simulated annealing info */
+ fprintf(out,"anneal%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10s",EANNEAL(opts->annealing[i]));
+ fprintf(out,"\n");
+
+ fprintf(out,"ann_npoints%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10d",opts->anneal_npoints[i]);
+ fprintf(out,"\n");
+
+ for(i=0; (i<opts->ngtc); i++) {
+ if(opts->anneal_npoints[i]>0) {
+ fprintf(out,"ann. times [%d]:\t",i);
+ for(j=0; (j<opts->anneal_npoints[i]); j++)
+ fprintf(out," %10.1f",opts->anneal_time[i][j]);
+ fprintf(out,"\n");
+ fprintf(out,"ann. temps [%d]:\t",i);
+ for(j=0; (j<opts->anneal_npoints[i]); j++)
+ fprintf(out," %10.1f",opts->anneal_temp[i][j]);
+ fprintf(out,"\n");
+ }
+ }
+
+ pr_indent(out,indent);
+ fprintf(out,"acc:\t");
+ for(i=0; (i<opts->ngacc); i++)
+ for(m=0; (m<DIM); m++)
+ fprintf(out," %10g",opts->acc[i][m]);
+ fprintf(out,"\n");
+
+ pr_indent(out,indent);
+ fprintf(out,"nfreeze:");
+ for(i=0; (i<opts->ngfrz); i++)
+ for(m=0; (m<DIM); m++)
+ fprintf(out," %10s",opts->nFreeze[i][m] ? "Y" : "N");
+ fprintf(out,"\n");
+
+
+ for(i=0; (i<opts->ngener); i++) {
+ pr_indent(out,indent);
+ fprintf(out,"energygrp_flags[%3d]:",i);
+ for(m=0; (m<opts->ngener); m++)
+ fprintf(out," %d",opts->egp_flags[opts->ngener*i+m]);
+ fprintf(out,"\n");
+ }
+
+ fflush(out);
+}
+
+static void pr_matrix(FILE *fp,int indent,const char *title,rvec *m,
+ gmx_bool bMDPformat)
+{
+ if (bMDPformat)
+ fprintf(fp,"%-10s = %g %g %g %g %g %g\n",title,
+ m[XX][XX],m[YY][YY],m[ZZ][ZZ],m[XX][YY],m[XX][ZZ],m[YY][ZZ]);
+ else
+ pr_rvecs(fp,indent,title,m,DIM);
+}
+
+static void pr_cosine(FILE *fp,int indent,const char *title,t_cosines *cos,
+ gmx_bool bMDPformat)
+{
+ int j;
+
+ if (bMDPformat) {
+ fprintf(fp,"%s = %d\n",title,cos->n);
+ }
+ else {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"n = %d\n",cos->n);
+ if (cos->n > 0) {
+ (void) pr_indent(fp,indent+2);
+ fprintf(fp,"a =");
+ for(j=0; (j<cos->n); j++)
+ fprintf(fp," %e",cos->a[j]);
+ fprintf(fp,"\n");
+ (void) pr_indent(fp,indent+2);
+ fprintf(fp,"phi =");
+ for(j=0; (j<cos->n); j++)
+ fprintf(fp," %e",cos->phi[j]);
+ fprintf(fp,"\n");
+ }
+ }
+}
+
+#define PS(t,s) pr_str(fp,indent,t,s)
+#define PI(t,s) pr_int(fp,indent,t,s)
+#define PSTEP(t,s) pr_gmx_large_int(fp,indent,t,s)
+#define PR(t,s) pr_real(fp,indent,t,s)
+#define PD(t,s) pr_double(fp,indent,t,s)
+
+static void pr_pullgrp(FILE *fp,int indent,int g,t_pullgrp *pg)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"pull_group %d:\n",g);
+ indent += 2;
+ pr_ivec_block(fp,indent,"atom",pg->ind,pg->nat,TRUE);
+ pr_rvec(fp,indent,"weight",pg->weight,pg->nweight,TRUE);
+ PI("pbcatom",pg->pbcatom);
+ pr_rvec(fp,indent,"vec",pg->vec,DIM,TRUE);
+ pr_rvec(fp,indent,"init",pg->init,DIM,TRUE);
+ PR("rate",pg->rate);
+ PR("k",pg->k);
+ PR("kB",pg->kB);
+}
+
+static void pr_pull(FILE *fp,int indent,t_pull *pull)
+{
+ int g;
+
+ PS("pull_geometry",EPULLGEOM(pull->eGeom));
+ pr_ivec(fp,indent,"pull_dim",pull->dim,DIM,TRUE);
+ PR("pull_r1",pull->cyl_r1);
+ PR("pull_r0",pull->cyl_r0);
+ PR("pull_constr_tol",pull->constr_tol);
+ PI("pull_nstxout",pull->nstxout);
+ PI("pull_nstfout",pull->nstfout);
+ PI("pull_ngrp",pull->ngrp);
+ for(g=0; g<pull->ngrp+1; g++)
+ pr_pullgrp(fp,indent,g,&pull->grp[g]);
+}
+
+static void pr_rotgrp(FILE *fp,int indent,int g,t_rotgrp *rotg)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"rotation_group %d:\n",g);
+ indent += 2;
+ PS("type",EROTGEOM(rotg->eType));
+ PS("massw",BOOL(rotg->bMassW));
+ pr_ivec_block(fp,indent,"atom",rotg->ind,rotg->nat,TRUE);
+ pr_rvecs(fp,indent,"x_ref",rotg->x_ref,rotg->nat);
+ pr_rvec(fp,indent,"vec",rotg->vec,DIM,TRUE);
+ pr_rvec(fp,indent,"pivot",rotg->pivot,DIM,TRUE);
+ PR("rate",rotg->rate);
+ PR("k",rotg->k);
+ PR("slab_dist",rotg->slab_dist);
+ PR("min_gaussian",rotg->min_gaussian);
+ PR("epsilon",rotg->eps);
+ PS("fit_method",EROTFIT(rotg->eFittype));
+}
+
+static void pr_rot(FILE *fp,int indent,t_rot *rot)
+{
+ int g;
+
+ PI("rot_nstrout",rot->nstrout);
+ PI("rot_nstsout",rot->nstsout);
+ PI("rot_ngrp",rot->ngrp);
+ for(g=0; g<rot->ngrp; g++)
+ pr_rotgrp(fp,indent,g,&rot->grp[g]);
+}
+
+void pr_inputrec(FILE *fp,int indent,const char *title,t_inputrec *ir,
+ gmx_bool bMDPformat)
+{
+ const char *infbuf="inf";
+ int i;
+
+ if (available(fp,ir,indent,title)) {
+ if (!bMDPformat)
+ indent=pr_title(fp,indent,title);
+ PS("integrator",EI(ir->eI));
+ PSTEP("nsteps",ir->nsteps);
+ PSTEP("init_step",ir->init_step);
+ PS("ns_type",ENS(ir->ns_type));
+ PI("nstlist",ir->nstlist);
+ PI("ndelta",ir->ndelta);
+ PI("nstcomm",ir->nstcomm);
+ PS("comm_mode",ECOM(ir->comm_mode));
+ PI("nstlog",ir->nstlog);
+ PI("nstxout",ir->nstxout);
+ PI("nstvout",ir->nstvout);
+ PI("nstfout",ir->nstfout);
+ PI("nstcalcenergy",ir->nstcalcenergy);
+ PI("nstenergy",ir->nstenergy);
+ PI("nstxtcout",ir->nstxtcout);
+ PR("init_t",ir->init_t);
+ PR("delta_t",ir->delta_t);
+
+ PR("xtcprec",ir->xtcprec);
+ PI("nkx",ir->nkx);
+ PI("nky",ir->nky);
+ PI("nkz",ir->nkz);
+ PI("pme_order",ir->pme_order);
+ PR("ewald_rtol",ir->ewald_rtol);
+ PR("ewald_geometry",ir->ewald_geometry);
+ PR("epsilon_surface",ir->epsilon_surface);
+ PS("optimize_fft",BOOL(ir->bOptFFT));
+ PS("ePBC",EPBC(ir->ePBC));
+ PS("bPeriodicMols",BOOL(ir->bPeriodicMols));
+ PS("bContinuation",BOOL(ir->bContinuation));
+ PS("bShakeSOR",BOOL(ir->bShakeSOR));
+ PS("etc",ETCOUPLTYPE(ir->etc));
+ PI("nsttcouple",ir->nsttcouple);
+ PS("epc",EPCOUPLTYPE(ir->epc));
+ PS("epctype",EPCOUPLTYPETYPE(ir->epct));
+ PI("nstpcouple",ir->nstpcouple);
+ PR("tau_p",ir->tau_p);
+ pr_matrix(fp,indent,"ref_p",ir->ref_p,bMDPformat);
+ pr_matrix(fp,indent,"compress",ir->compress,bMDPformat);
+ PS("refcoord_scaling",EREFSCALINGTYPE(ir->refcoord_scaling));
+ if (bMDPformat)
+ fprintf(fp,"posres_com = %g %g %g\n",ir->posres_com[XX],
+ ir->posres_com[YY],ir->posres_com[ZZ]);
+ else
+ pr_rvec(fp,indent,"posres_com",ir->posres_com,DIM,TRUE);
+ if (bMDPformat)
+ fprintf(fp,"posres_comB = %g %g %g\n",ir->posres_comB[XX],
+ ir->posres_comB[YY],ir->posres_comB[ZZ]);
+ else
+ pr_rvec(fp,indent,"posres_comB",ir->posres_comB,DIM,TRUE);
+ PI("andersen_seed",ir->andersen_seed);
+ PR("rlist",ir->rlist);
+ PR("rlistlong",ir->rlistlong);
+ PR("rtpi",ir->rtpi);
+ PS("coulombtype",EELTYPE(ir->coulombtype));
+ PR("rcoulomb_switch",ir->rcoulomb_switch);
+ PR("rcoulomb",ir->rcoulomb);
+ PS("vdwtype",EVDWTYPE(ir->vdwtype));
+ PR("rvdw_switch",ir->rvdw_switch);
+ PR("rvdw",ir->rvdw);
+ if (ir->epsilon_r != 0)
+ PR("epsilon_r",ir->epsilon_r);
+ else
+ PS("epsilon_r",infbuf);
+ if (ir->epsilon_rf != 0)
+ PR("epsilon_rf",ir->epsilon_rf);
+ else
+ PS("epsilon_rf",infbuf);
+ PR("tabext",ir->tabext);
+ PS("implicit_solvent",EIMPLICITSOL(ir->implicit_solvent));
+ PS("gb_algorithm",EGBALGORITHM(ir->gb_algorithm));
+ PR("gb_epsilon_solvent",ir->gb_epsilon_solvent);
+ PI("nstgbradii",ir->nstgbradii);
+ PR("rgbradii",ir->rgbradii);
+ PR("gb_saltconc",ir->gb_saltconc);
+ PR("gb_obc_alpha",ir->gb_obc_alpha);
+ PR("gb_obc_beta",ir->gb_obc_beta);
+ PR("gb_obc_gamma",ir->gb_obc_gamma);
+ PR("gb_dielectric_offset",ir->gb_dielectric_offset);
+ PS("sa_algorithm",ESAALGORITHM(ir->gb_algorithm));
+ PR("sa_surface_tension",ir->sa_surface_tension);
+
+ PS("DispCorr",EDISPCORR(ir->eDispCorr));
+ PS("free_energy",EFEPTYPE(ir->efep));
+ PR("init_lambda",ir->init_lambda);
+ PR("delta_lambda",ir->delta_lambda);
+ if (!bMDPformat)
+ {
+ PI("n_foreign_lambda",ir->n_flambda);
+ }
+ if (ir->n_flambda > 0)
+ {
+ pr_indent(fp,indent);
+ fprintf(fp,"foreign_lambda%s",bMDPformat ? " = " : ":");
+ for(i=0; i<ir->n_flambda; i++)
+ {
+ fprintf(fp," %10g",ir->flambda[i]);
+ }
+ fprintf(fp,"\n");
+ }
+ PR("sc_alpha",ir->sc_alpha);
+ PI("sc_power",ir->sc_power);
+ PR("sc_sigma",ir->sc_sigma);
+ PR("sc_sigma_min",ir->sc_sigma_min);
+ PI("nstdhdl", ir->nstdhdl);
+ PS("separate_dhdl_file", SEPDHDLFILETYPE(ir->separate_dhdl_file));
+ PS("dhdl_derivatives", DHDLDERIVATIVESTYPE(ir->dhdl_derivatives));
+ PI("dh_hist_size", ir->dh_hist_size);
+ PD("dh_hist_spacing", ir->dh_hist_spacing);
+
+ PI("nwall",ir->nwall);
+ PS("wall_type",EWALLTYPE(ir->wall_type));
+ PI("wall_atomtype[0]",ir->wall_atomtype[0]);
+ PI("wall_atomtype[1]",ir->wall_atomtype[1]);
+ PR("wall_density[0]",ir->wall_density[0]);
+ PR("wall_density[1]",ir->wall_density[1]);
+ PR("wall_ewald_zfac",ir->wall_ewald_zfac);
+
+ PS("pull",EPULLTYPE(ir->ePull));
+ if (ir->ePull != epullNO)
+ pr_pull(fp,indent,ir->pull);
+
+ PS("rotation",BOOL(ir->bRot));
+ if (ir->bRot)
+ pr_rot(fp,indent,ir->rot);
+
+ PS("disre",EDISRETYPE(ir->eDisre));
+ PS("disre_weighting",EDISREWEIGHTING(ir->eDisreWeighting));
+ PS("disre_mixed",BOOL(ir->bDisreMixed));
+ PR("dr_fc",ir->dr_fc);
+ PR("dr_tau",ir->dr_tau);
+ PR("nstdisreout",ir->nstdisreout);
+ PR("orires_fc",ir->orires_fc);
+ PR("orires_tau",ir->orires_tau);
+ PR("nstorireout",ir->nstorireout);
+
+ PR("dihre-fc",ir->dihre_fc);
+
+ PR("em_stepsize",ir->em_stepsize);
+ PR("em_tol",ir->em_tol);
+ PI("niter",ir->niter);
+ PR("fc_stepsize",ir->fc_stepsize);
+ PI("nstcgsteep",ir->nstcgsteep);
+ PI("nbfgscorr",ir->nbfgscorr);
+
+ PS("ConstAlg",ECONSTRTYPE(ir->eConstrAlg));
+ PR("shake_tol",ir->shake_tol);
+ PI("lincs_order",ir->nProjOrder);
+ PR("lincs_warnangle",ir->LincsWarnAngle);
+ PI("lincs_iter",ir->nLincsIter);
+ PR("bd_fric",ir->bd_fric);
+ PI("ld_seed",ir->ld_seed);
+ PR("cos_accel",ir->cos_accel);
+ pr_matrix(fp,indent,"deform",ir->deform,bMDPformat);
+ PI("userint1",ir->userint1);
+ PI("userint2",ir->userint2);
+ PI("userint3",ir->userint3);
+ PI("userint4",ir->userint4);
+ PR("userreal1",ir->userreal1);
+ PR("userreal2",ir->userreal2);
+ PR("userreal3",ir->userreal3);
+ PR("userreal4",ir->userreal4);
+ pr_grp_opts(fp,indent,"grpopts",&(ir->opts),bMDPformat);
+ pr_cosine(fp,indent,"efield-x",&(ir->ex[XX]),bMDPformat);
+ pr_cosine(fp,indent,"efield-xt",&(ir->et[XX]),bMDPformat);
+ pr_cosine(fp,indent,"efield-y",&(ir->ex[YY]),bMDPformat);
+ pr_cosine(fp,indent,"efield-yt",&(ir->et[YY]),bMDPformat);
+ pr_cosine(fp,indent,"efield-z",&(ir->ex[ZZ]),bMDPformat);
+ pr_cosine(fp,indent,"efield-zt",&(ir->et[ZZ]),bMDPformat);
+ PS("bQMMM",BOOL(ir->bQMMM));
+ PI("QMconstraints",ir->QMconstraints);
+ PI("QMMMscheme",ir->QMMMscheme);
+ PR("scalefactor",ir->scalefactor);
+ pr_qm_opts(fp,indent,"qm_opts",&(ir->opts));
+ }
+}
+#undef PS
+#undef PR
+#undef PI
+
+static void pr_harm(FILE *fp,t_iparams *iparams,const char *r,const char *kr)
+{
+ fprintf(fp,"%sA=%12.5e, %sA=%12.5e, %sB=%12.5e, %sB=%12.5e\n",
+ r,iparams->harmonic.rA,kr,iparams->harmonic.krA,
+ r,iparams->harmonic.rB,kr,iparams->harmonic.krB);
+}
+
+void pr_iparams(FILE *fp,t_functype ftype,t_iparams *iparams)
+{
+ int i;
+ real VA[4],VB[4],*rbcA,*rbcB;
+
+ switch (ftype) {
+ case F_ANGLES:
+ case F_G96ANGLES:
+ pr_harm(fp,iparams,"th","ct");
+ break;
+ case F_CROSS_BOND_BONDS:
+ fprintf(fp,"r1e=%15.8e, r2e=%15.8e, krr=%15.8e\n",
+ iparams->cross_bb.r1e,iparams->cross_bb.r2e,
+ iparams->cross_bb.krr);
+ break;
+ case F_CROSS_BOND_ANGLES:
+ fprintf(fp,"r1e=%15.8e, r1e=%15.8e, r3e=%15.8e, krt=%15.8e\n",
+ iparams->cross_ba.r1e,iparams->cross_ba.r2e,
+ iparams->cross_ba.r3e,iparams->cross_ba.krt);
+ break;
+ case F_UREY_BRADLEY:
+ fprintf(fp,"theta=%15.8e, ktheta=%15.8e, r13=%15.8e, kUB=%15.8e\n",
+ iparams->u_b.theta,iparams->u_b.ktheta,iparams->u_b.r13,iparams->u_b.kUB);
+ break;
+ case F_QUARTIC_ANGLES:
+ fprintf(fp,"theta=%15.8e",iparams->qangle.theta);
+ for(i=0; i<5; i++)
+ fprintf(fp,", c%c=%15.8e",'0'+i,iparams->qangle.c[i]);
+ fprintf(fp,"\n");
+ break;
+ case F_BHAM:
+ fprintf(fp,"a=%15.8e, b=%15.8e, c=%15.8e\n",
+ iparams->bham.a,iparams->bham.b,iparams->bham.c);
+ break;
+ case F_BONDS:
+ case F_G96BONDS:
+ case F_HARMONIC:
+ pr_harm(fp,iparams,"b0","cb");
+ break;
+ case F_IDIHS:
+ pr_harm(fp,iparams,"xi","cx");
+ break;
+ case F_MORSE:
+ fprintf(fp,"b0=%15.8e, cb=%15.8e, beta=%15.8e\n",
+ iparams->morse.b0,iparams->morse.cb,iparams->morse.beta);
+ break;
+ case F_CUBICBONDS:
+ fprintf(fp,"b0=%15.8e, kb=%15.8e, kcub=%15.8e\n",
+ iparams->cubic.b0,iparams->cubic.kb,iparams->cubic.kcub);
+ break;
+ case F_CONNBONDS:
+ fprintf(fp,"\n");
+ break;
+ case F_FENEBONDS:
+ fprintf(fp,"bm=%15.8e, kb=%15.8e\n",iparams->fene.bm,iparams->fene.kb);
+ break;
+ case F_RESTRBONDS:
+ fprintf(fp,"lowA=%15.8e, up1A=%15.8e, up2A=%15.8e, kA=%15.8e, lowB=%15.8e, up1B=%15.8e, up2B=%15.8e, kB=%15.8e,\n",
+ iparams->restraint.lowA,iparams->restraint.up1A,
+ iparams->restraint.up2A,iparams->restraint.kA,
+ iparams->restraint.lowB,iparams->restraint.up1B,
+ iparams->restraint.up2B,iparams->restraint.kB);
+ break;
+ case F_TABBONDS:
+ case F_TABBONDSNC:
+ case F_TABANGLES:
+ case F_TABDIHS:
+ fprintf(fp,"tab=%d, kA=%15.8e, kB=%15.8e\n",
+ iparams->tab.table,iparams->tab.kA,iparams->tab.kB);
+ break;
+ case F_POLARIZATION:
+ fprintf(fp,"alpha=%15.8e\n",iparams->polarize.alpha);
+ break;
+ case F_THOLE_POL:
+ fprintf(fp,"a=%15.8e, alpha1=%15.8e, alpha2=%15.8e, rfac=%15.8e\n",
+ iparams->thole.a,iparams->thole.alpha1,iparams->thole.alpha2,
+ iparams->thole.rfac);
+ break;
+ case F_WATER_POL:
+ fprintf(fp,"al_x=%15.8e, al_y=%15.8e, al_z=%15.8e, rOH=%9.6f, rHH=%9.6f, rOD=%9.6f\n",
+ iparams->wpol.al_x,iparams->wpol.al_y,iparams->wpol.al_z,
+ iparams->wpol.rOH,iparams->wpol.rHH,iparams->wpol.rOD);
+ break;
+ case F_LJ:
+ fprintf(fp,"c6=%15.8e, c12=%15.8e\n",iparams->lj.c6,iparams->lj.c12);
+ break;
+ case F_LJ14:
+ fprintf(fp,"c6A=%15.8e, c12A=%15.8e, c6B=%15.8e, c12B=%15.8e\n",
+ iparams->lj14.c6A,iparams->lj14.c12A,
+ iparams->lj14.c6B,iparams->lj14.c12B);
+ break;
+ case F_LJC14_Q:
+ fprintf(fp,"fqq=%15.8e, qi=%15.8e, qj=%15.8e, c6=%15.8e, c12=%15.8e\n",
+ iparams->ljc14.fqq,
+ iparams->ljc14.qi,iparams->ljc14.qj,
+ iparams->ljc14.c6,iparams->ljc14.c12);
+ break;
+ case F_LJC_PAIRS_NB:
+ fprintf(fp,"qi=%15.8e, qj=%15.8e, c6=%15.8e, c12=%15.8e\n",
+ iparams->ljcnb.qi,iparams->ljcnb.qj,
+ iparams->ljcnb.c6,iparams->ljcnb.c12);
+ break;
+ case F_PDIHS:
+ case F_PIDIHS:
+ case F_ANGRES:
+ case F_ANGRESZ:
+ fprintf(fp,"phiA=%15.8e, cpA=%15.8e, phiB=%15.8e, cpB=%15.8e, mult=%d\n",
+ iparams->pdihs.phiA,iparams->pdihs.cpA,
+ iparams->pdihs.phiB,iparams->pdihs.cpB,
+ iparams->pdihs.mult);
+ break;
+ case F_DISRES:
+ fprintf(fp,"label=%4d, type=%1d, low=%15.8e, up1=%15.8e, up2=%15.8e, fac=%15.8e)\n",
+ iparams->disres.label,iparams->disres.type,
+ iparams->disres.low,iparams->disres.up1,
+ iparams->disres.up2,iparams->disres.kfac);
+ break;
+ case F_ORIRES:
+ fprintf(fp,"ex=%4d, label=%d, power=%4d, c=%15.8e, obs=%15.8e, kfac=%15.8e)\n",
+ iparams->orires.ex,iparams->orires.label,iparams->orires.power,
+ iparams->orires.c,iparams->orires.obs,iparams->orires.kfac);
+ break;
+ case F_DIHRES:
+ fprintf(fp,"label=%d, power=%4d phi=%15.8e, dphi=%15.8e, kfac=%15.8e)\n",
+ iparams->dihres.label,iparams->dihres.power,
+ iparams->dihres.phi,iparams->dihres.dphi,iparams->dihres.kfac);
+ break;
+ case F_POSRES:
+ fprintf(fp,"pos0A=(%15.8e,%15.8e,%15.8e), fcA=(%15.8e,%15.8e,%15.8e), pos0B=(%15.8e,%15.8e,%15.8e), fcB=(%15.8e,%15.8e,%15.8e)\n",
+ iparams->posres.pos0A[XX],iparams->posres.pos0A[YY],
+ iparams->posres.pos0A[ZZ],iparams->posres.fcA[XX],
+ iparams->posres.fcA[YY],iparams->posres.fcA[ZZ],
+ iparams->posres.pos0B[XX],iparams->posres.pos0B[YY],
+ iparams->posres.pos0B[ZZ],iparams->posres.fcB[XX],
+ iparams->posres.fcB[YY],iparams->posres.fcB[ZZ]);
+ break;
+ case F_RBDIHS:
+ for (i=0; i<NR_RBDIHS; i++)
+ fprintf(fp,"%srbcA[%d]=%15.8e",i==0?"":", ",i,iparams->rbdihs.rbcA[i]);
+ fprintf(fp,"\n");
+ for (i=0; i<NR_RBDIHS; i++)
+ fprintf(fp,"%srbcB[%d]=%15.8e",i==0?"":", ",i,iparams->rbdihs.rbcB[i]);
+ fprintf(fp,"\n");
+ break;
+ case F_FOURDIHS:
+ /* Use the OPLS -> Ryckaert-Bellemans formula backwards to get the
+ * OPLS potential constants back.
+ */
+ rbcA = iparams->rbdihs.rbcA;
+ rbcB = iparams->rbdihs.rbcB;
+
+ VA[3] = -0.25*rbcA[4];
+ VA[2] = -0.5*rbcA[3];
+ VA[1] = 4.0*VA[3]-rbcA[2];
+ VA[0] = 3.0*VA[2]-2.0*rbcA[1];
+
+ VB[3] = -0.25*rbcB[4];
+ VB[2] = -0.5*rbcB[3];
+ VB[1] = 4.0*VB[3]-rbcB[2];
+ VB[0] = 3.0*VB[2]-2.0*rbcB[1];
+
+ for (i=0; i<NR_FOURDIHS; i++)
+ fprintf(fp,"%sFourA[%d]=%15.8e",i==0?"":", ",i,VA[i]);
+ fprintf(fp,"\n");
+ for (i=0; i<NR_FOURDIHS; i++)
+ fprintf(fp,"%sFourB[%d]=%15.8e",i==0?"":", ",i,VB[i]);
+ fprintf(fp,"\n");
+ break;
+
+ case F_CONSTR:
+ case F_CONSTRNC:
+ fprintf(fp,"dA=%15.8e, dB=%15.8e\n",iparams->constr.dA,iparams->constr.dB);
+ break;
+ case F_SETTLE:
+ fprintf(fp,"doh=%15.8e, dhh=%15.8e\n",iparams->settle.doh,
+ iparams->settle.dhh);
+ break;
+ case F_VSITE2:
+ fprintf(fp,"a=%15.8e\n",iparams->vsite.a);
+ break;
+ case F_VSITE3:
+ case F_VSITE3FD:
+ case F_VSITE3FAD:
+ fprintf(fp,"a=%15.8e, b=%15.8e\n",iparams->vsite.a,iparams->vsite.b);
+ break;
+ case F_VSITE3OUT:
+ case F_VSITE4FD:
+ case F_VSITE4FDN:
+ fprintf(fp,"a=%15.8e, b=%15.8e, c=%15.8e\n",
+ iparams->vsite.a,iparams->vsite.b,iparams->vsite.c);
+ break;
+ case F_VSITEN:
+ fprintf(fp,"n=%2d, a=%15.8e\n",iparams->vsiten.n,iparams->vsiten.a);
+ break;
+ case F_GB12:
+ case F_GB13:
+ case F_GB14:
+ fprintf(fp, "sar=%15.8e, st=%15.8e, pi=%15.8e, gbr=%15.8e, bmlt=%15.8e\n",iparams->gb.sar,iparams->gb.st,iparams->gb.pi,iparams->gb.gbr,iparams->gb.bmlt);
+ break;
+ case F_CMAP:
+ fprintf(fp, "cmapA=%1d, cmapB=%1d\n",iparams->cmap.cmapA, iparams->cmap.cmapB);
+ break;
+ default:
+ gmx_fatal(FARGS,"unknown function type %d (%s) in %s line %d",
+ ftype,interaction_function[ftype].name,__FILE__,__LINE__);
+ }
+}
+
+void pr_ilist(FILE *fp,int indent,const char *title,
+ t_functype *functype,t_ilist *ilist, gmx_bool bShowNumbers)
+{
+ int i,j,k,type,ftype;
+ t_iatom *iatoms;
+
+ if (available(fp,ilist,indent,title) && ilist->nr > 0)
+ {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"nr: %d\n",ilist->nr);
+ if (ilist->nr > 0) {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"iatoms:\n");
+ iatoms=ilist->iatoms;
+ for (i=j=0; i<ilist->nr;) {
+#ifndef DEBUG
+ (void) pr_indent(fp,indent+INDENT);
+ type=*(iatoms++);
+ ftype=functype[type];
+ (void) fprintf(fp,"%d type=%d (%s)",
+ bShowNumbers?j:-1,bShowNumbers?type:-1,
+ interaction_function[ftype].name);
+ j++;
+ for (k=0; k<interaction_function[ftype].nratoms; k++)
+ (void) fprintf(fp," %u",*(iatoms++));
+ (void) fprintf(fp,"\n");
+ i+=1+interaction_function[ftype].nratoms;
+#else
+ fprintf(fp,"%5d%5d\n",i,iatoms[i]);
+ i++;
+#endif
+ }
+ }
+ }
+}
+
+static void pr_cmap(FILE *fp, int indent, const char *title,
+ gmx_cmap_t *cmap_grid, gmx_bool bShowNumbers)
+{
+ int i,j,nelem;
+ real dx,idx;
+
+ dx = 360.0 / cmap_grid->grid_spacing;
+ nelem = cmap_grid->grid_spacing*cmap_grid->grid_spacing;
+
+ if(available(fp,cmap_grid,indent,title))
+ {
+ fprintf(fp,"%s\n",title);
+
+ for(i=0;i<cmap_grid->ngrid;i++)
+ {
+ idx = -180.0;
+ fprintf(fp,"%8s %8s %8s %8s\n","V","dVdx","dVdy","d2dV");
+
+ fprintf(fp,"grid[%3d]={\n",bShowNumbers?i:-1);
+
+ for(j=0;j<nelem;j++)
+ {
+ if( (j%cmap_grid->grid_spacing)==0)
+ {
+ fprintf(fp,"%8.1f\n",idx);
+ idx+=dx;
+ }
+
+ fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4]);
+ fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4+1]);
+ fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4+2]);
+ fprintf(fp,"%8.3f\n",cmap_grid->cmapdata[i].cmap[j*4+3]);
+ }
+ fprintf(fp,"\n");
+ }
+ }
+
+}
+
+void pr_ffparams(FILE *fp,int indent,const char *title,
+ gmx_ffparams_t *ffparams,
+ gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"atnr=%d\n",ffparams->atnr);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"ntypes=%d\n",ffparams->ntypes);
+ for (i=0; i<ffparams->ntypes; i++) {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"functype[%d]=%s, ",
+ bShowNumbers?i:-1,
+ interaction_function[ffparams->functype[i]].name);
+ pr_iparams(fp,ffparams->functype[i],&ffparams->iparams[i]);
+ }
+ (void) pr_double(fp,indent,"reppow",ffparams->reppow);
+ (void) pr_real(fp,indent,"fudgeQQ",ffparams->fudgeQQ);
+ pr_cmap(fp,indent,"cmap",&ffparams->cmap_grid,bShowNumbers);
+}
+
+void pr_idef(FILE *fp,int indent,const char *title,t_idef *idef, gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ if (available(fp,idef,indent,title)) {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"atnr=%d\n",idef->atnr);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"ntypes=%d\n",idef->ntypes);
+ for (i=0; i<idef->ntypes; i++) {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"functype[%d]=%s, ",
+ bShowNumbers?i:-1,
+ interaction_function[idef->functype[i]].name);
+ pr_iparams(fp,idef->functype[i],&idef->iparams[i]);
+ }
+ (void) pr_real(fp,indent,"fudgeQQ",idef->fudgeQQ);
+
+ for(j=0; (j<F_NRE); j++)
+ pr_ilist(fp,indent,interaction_function[j].longname,
+ idef->functype,&idef->il[j],bShowNumbers);
+ }
+}
+
+static int pr_block_title(FILE *fp,int indent,const char *title,t_block *block)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"nr=%d\n",block->nr);
+ }
+ return indent;
+}
+
+static int pr_blocka_title(FILE *fp,int indent,const char *title,t_blocka *block)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"nr=%d\n",block->nr);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"nra=%d\n",block->nra);
+ }
+ return indent;
+}
+
+static void low_pr_block(FILE *fp,int indent,const char *title,t_block *block, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_block_title(fp,indent,title,block);
+ for (i=0; i<=block->nr; i++)
+ {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"%s->index[%d]=%u\n",
+ title,bShowNumbers?i:-1,block->index[i]);
+ }
+ }
+}
+
+static void low_pr_blocka(FILE *fp,int indent,const char *title,t_blocka *block, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_blocka_title(fp,indent,title,block);
+ for (i=0; i<=block->nr; i++)
+ {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"%s->index[%d]=%u\n",
+ title,bShowNumbers?i:-1,block->index[i]);
+ }
+ for (i=0; i<block->nra; i++)
+ {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"%s->a[%d]=%u\n",
+ title,bShowNumbers?i:-1,block->a[i]);
+ }
+ }
+}
+
+void pr_block(FILE *fp,int indent,const char *title,t_block *block,gmx_bool bShowNumbers)
+{
+ int i,j,ok,size,start,end;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_block_title(fp,indent,title,block);
+ start=0;
+ end=start;
+ if ((ok=(block->index[start]==0))==0)
+ (void) fprintf(fp,"block->index[%d] should be 0\n",start);
+ else
+ for (i=0; i<block->nr; i++)
+ {
+ end=block->index[i+1];
+ size=pr_indent(fp,indent);
+ if (end<=start)
+ size+=fprintf(fp,"%s[%d]={}\n",title,i);
+ else
+ size+=fprintf(fp,"%s[%d]={%d..%d}\n",
+ title,bShowNumbers?i:-1,
+ bShowNumbers?start:-1,bShowNumbers?end-1:-1);
+ start=end;
+ }
+ }
+}
+
+void pr_blocka(FILE *fp,int indent,const char *title,t_blocka *block,gmx_bool bShowNumbers)
+{
+ int i,j,ok,size,start,end;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_blocka_title(fp,indent,title,block);
+ start=0;
+ end=start;
+ if ((ok=(block->index[start]==0))==0)
+ (void) fprintf(fp,"block->index[%d] should be 0\n",start);
+ else
+ for (i=0; i<block->nr; i++)
+ {
+ end=block->index[i+1];
+ size=pr_indent(fp,indent);
+ if (end<=start)
+ size+=fprintf(fp,"%s[%d]={",title,i);
+ else
+ size+=fprintf(fp,"%s[%d][%d..%d]={",
+ title,bShowNumbers?i:-1,
+ bShowNumbers?start:-1,bShowNumbers?end-1:-1);
+ for (j=start; j<end; j++)
+ {
+ if (j>start) size+=fprintf(fp,", ");
+ if ((size)>(USE_WIDTH))
+ {
+ (void) fprintf(fp,"\n");
+ size=pr_indent(fp,indent+INDENT);
+ }
+ size+=fprintf(fp,"%u",block->a[j]);
+ }
+ (void) fprintf(fp,"}\n");
+ start=end;
+ }
+ if ((end!=block->nra)||(!ok))
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"tables inconsistent, dumping complete tables:\n");
+ low_pr_blocka(fp,indent,title,block,bShowNumbers);
+ }
+ }
+}
+
+static void pr_strings(FILE *fp,int indent,const char *title,char ***nm,int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,nm,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={name=\"%s\"}\n",
+ title,bShowNumbers?i:-1,*(nm[i]));
+ }
+ }
+}
+
+static void pr_strings2(FILE *fp,int indent,const char *title,
+ char ***nm,char ***nmB,int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,nm,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={name=\"%s\",nameB=\"%s\"}\n",
+ title,bShowNumbers?i:-1,*(nm[i]),*(nmB[i]));
+ }
+ }
+}
+
+static void pr_resinfo(FILE *fp,int indent,const char *title,t_resinfo *resinfo,int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,resinfo,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={name=\"%s\", nr=%d, ic='%c'}\n",
+ title,bShowNumbers?i:-1,
+ *(resinfo[i].name),resinfo[i].nr,
+ (resinfo[i].ic == '\0') ? ' ' : resinfo[i].ic);
+ }
+ }
+}
+
+static void pr_atom(FILE *fp,int indent,const char *title,t_atom *atom,int n)
+{
+ int i,j;
+
+ if (available(fp,atom,indent,title)) {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++) {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"%s[%6d]={type=%3d, typeB=%3d, ptype=%8s, m=%12.5e, "
+ "q=%12.5e, mB=%12.5e, qB=%12.5e, resind=%5d, atomnumber=%3d}\n",
+ title,i,atom[i].type,atom[i].typeB,ptype_str[atom[i].ptype],
+ atom[i].m,atom[i].q,atom[i].mB,atom[i].qB,
+ atom[i].resind,atom[i].atomnumber);
+ }
+ }
+}
+
+static void pr_grps(FILE *fp,int indent,const char *title,t_grps grps[],
+ char **grpname[], gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ for(i=0; (i<egcNR); i++)
+ {
+ fprintf(fp,"%s[%-12s] nr=%d, name=[",title,gtypes[i],grps[i].nr);
+ for(j=0; (j<grps[i].nr); j++)
+ {
+ fprintf(fp," %s",*(grpname[grps[i].nm_ind[j]]));
+ }
+ fprintf(fp,"]\n");
+ }
+}
+
+static void pr_groups(FILE *fp,int indent,const char *title,
+ gmx_groups_t *groups,
+ gmx_bool bShowNumbers)
+{
+ int grpnr[egcNR];
+ int nat_max,i,g;
+
+ pr_grps(fp,indent,"grp",groups->grps,groups->grpname,bShowNumbers);
+ pr_strings(fp,indent,"grpname",groups->grpname,groups->ngrpname,bShowNumbers);
+
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"groups ");
+ for(g=0; g<egcNR; g++)
+ {
+ printf(" %5.5s",gtypes[g]);
+ }
+ printf("\n");
+
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"allocated ");
+ nat_max = 0;
+ for(g=0; g<egcNR; g++)
+ {
+ printf(" %5d",groups->ngrpnr[g]);
+ nat_max = max(nat_max,groups->ngrpnr[g]);
+ }
+ printf("\n");
+
+ if (nat_max == 0)
+ {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"groupnr[%5s] =","*");
+ for(g=0; g<egcNR; g++)
+ {
+ fprintf(fp," %3d ",0);
+ }
+ fprintf(fp,"\n");
+ }
+ else
+ {
+ for(i=0; i<nat_max; i++)
+ {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"groupnr[%5d] =",i);
+ for(g=0; g<egcNR; g++)
+ {
+ fprintf(fp," %3d ",
+ groups->grpnr[g] ? groups->grpnr[g][i] : 0);
+ }
+ fprintf(fp,"\n");
+ }
+ }
+}
+
+void pr_atoms(FILE *fp,int indent,const char *title,t_atoms *atoms,
+ gmx_bool bShownumbers)
+{
+ if (available(fp,atoms,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ pr_atom(fp,indent,"atom",atoms->atom,atoms->nr);
+ pr_strings(fp,indent,"atom",atoms->atomname,atoms->nr,bShownumbers);
+ pr_strings2(fp,indent,"type",atoms->atomtype,atoms->atomtypeB,atoms->nr,bShownumbers);
+ pr_resinfo(fp,indent,"residue",atoms->resinfo,atoms->nres,bShownumbers);
+ }
+}
+
+
+void pr_atomtypes(FILE *fp,int indent,const char *title,t_atomtypes *atomtypes,
+ gmx_bool bShowNumbers)
+{
+ int i;
+ if (available(fp,atomtypes,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ for(i=0;i<atomtypes->nr;i++) {
+ pr_indent(fp,indent);
+ fprintf(fp,
+ "atomtype[%3d]={radius=%12.5e, volume=%12.5e, gb_radius=%12.5e, surftens=%12.5e, atomnumber=%4d, S_hct=%12.5e)}\n",
+ bShowNumbers?i:-1,atomtypes->radius[i],atomtypes->vol[i],
+ atomtypes->gb_radius[i],
+ atomtypes->surftens[i],atomtypes->atomnumber[i],atomtypes->S_hct[i]);
+ }
+ }
+}
+
+static void pr_moltype(FILE *fp,int indent,const char *title,
+ gmx_moltype_t *molt,int n,
+ gmx_ffparams_t *ffparams,
+ gmx_bool bShowNumbers)
+{
+ int j;
+
+ indent = pr_title_n(fp,indent,title,n);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"name=\"%s\"\n",*(molt->name));
+ pr_atoms(fp,indent,"atoms",&(molt->atoms),bShowNumbers);
+ pr_block(fp,indent,"cgs",&molt->cgs, bShowNumbers);
+ pr_blocka(fp,indent,"excls",&molt->excls, bShowNumbers);
+ for(j=0; (j<F_NRE); j++) {
+ pr_ilist(fp,indent,interaction_function[j].longname,
+ ffparams->functype,&molt->ilist[j],bShowNumbers);
+ }
+}
+
+static void pr_molblock(FILE *fp,int indent,const char *title,
+ gmx_molblock_t *molb,int n,
+ gmx_moltype_t *molt,
+ gmx_bool bShowNumbers)
+{
+ indent = pr_title_n(fp,indent,title,n);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%-20s = %d \"%s\"\n",
+ "moltype",molb->type,*(molt[molb->type].name));
+ pr_int(fp,indent,"#molecules",molb->nmol);
+ pr_int(fp,indent,"#atoms_mol",molb->natoms_mol);
+ pr_int(fp,indent,"#posres_xA",molb->nposres_xA);
+ if (molb->nposres_xA > 0) {
+ pr_rvecs(fp,indent,"posres_xA",molb->posres_xA,molb->nposres_xA);
+ }
+ pr_int(fp,indent,"#posres_xB",molb->nposres_xB);
+ if (molb->nposres_xB > 0) {
+ pr_rvecs(fp,indent,"posres_xB",molb->posres_xB,molb->nposres_xB);
+ }
+}
+
+void pr_mtop(FILE *fp,int indent,const char *title,gmx_mtop_t *mtop,
+ gmx_bool bShowNumbers)
+{
+ int mt,mb;
+
+ if (available(fp,mtop,indent,title)) {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"name=\"%s\"\n",*(mtop->name));
+ pr_int(fp,indent,"#atoms",mtop->natoms);
+ for(mb=0; mb<mtop->nmolblock; mb++) {
+ pr_molblock(fp,indent,"molblock",&mtop->molblock[mb],mb,
+ mtop->moltype,bShowNumbers);
+ }
+ pr_ffparams(fp,indent,"ffparams",&(mtop->ffparams),bShowNumbers);
+ pr_atomtypes(fp,indent,"atomtypes",&(mtop->atomtypes),bShowNumbers);
+ for(mt=0; mt<mtop->nmoltype; mt++) {
+ pr_moltype(fp,indent,"moltype",&mtop->moltype[mt],mt,
+ &mtop->ffparams,bShowNumbers);
+ }
+ pr_groups(fp,indent,"groups",&mtop->groups,bShowNumbers);
+ }
+}
+
+void pr_top(FILE *fp,int indent,const char *title,t_topology *top, gmx_bool bShowNumbers)
+{
+ if (available(fp,top,indent,title)) {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"name=\"%s\"\n",*(top->name));
+ pr_atoms(fp,indent,"atoms",&(top->atoms),bShowNumbers);
+ pr_atomtypes(fp,indent,"atomtypes",&(top->atomtypes),bShowNumbers);
+ pr_block(fp,indent,"cgs",&top->cgs, bShowNumbers);
+ pr_block(fp,indent,"mols",&top->mols, bShowNumbers);
+ pr_blocka(fp,indent,"excls",&top->excls, bShowNumbers);
+ pr_idef(fp,indent,"idef",&top->idef,bShowNumbers);
+ }
+}
+
+void pr_header(FILE *fp,int indent,const char *title,t_tpxheader *sh)
+{
+ char buf[22];
+
+ if (available(fp,sh,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ pr_indent(fp,indent);
+ fprintf(fp,"bIr = %spresent\n",sh->bIr?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bBox = %spresent\n",sh->bBox?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bTop = %spresent\n",sh->bTop?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bX = %spresent\n",sh->bX?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bV = %spresent\n",sh->bV?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bF = %spresent\n",sh->bF?"":"not ");
+
+ pr_indent(fp,indent);
+ fprintf(fp,"natoms = %d\n",sh->natoms);
+ pr_indent(fp,indent);
+ fprintf(fp,"lambda = %e\n",sh->lambda);
+ }
+}
+
+void pr_commrec(FILE *fp,int indent,t_commrec *cr)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"commrec:\n");
+ indent+=2;
+ pr_indent(fp,indent);
+ fprintf(fp,"nodeid = %d\n",cr->nodeid);
+ pr_indent(fp,indent);
+ fprintf(fp,"nnodes = %d\n",cr->nnodes);
+ pr_indent(fp,indent);
+ fprintf(fp,"npmenodes = %d\n",cr->npmenodes);
+ /*
+ pr_indent(fp,indent);
+ fprintf(fp,"threadid = %d\n",cr->threadid);
+ pr_indent(fp,indent);
+ fprintf(fp,"nthreads = %d\n",cr->nthreads);
+ */
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Dummy header for Doxygen documentation.
+ *
+ * This file just holds the main page for doxygen. When, at some point in the
+ * future, there is an obvious location for this documentation, we should
+ * move it there.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+/*! \mainpage Gromacs
+ * GROMACS is a versatile package to perform molecular dynamics, i.e. simulate
+ * the Newtonian equations of motion for systems with hundreds, to millions
+ * of particles.
+ */
+/*! \defgroup group_publicapi Public API
+ * \brief
+ * Classes and other symbols that are publicly accessible from user code.
+ */
+/*! \defgroup group_libraryapi Library API
+ * \brief
+ * Classes and other symbols that are publicly accessible within the Gromacs
+ * library.
+ *
+ * \see group_publicapi
+ */
+/*! \defgroup group_utilitymodules Utility Modules
+ * \brief
+ * Modules with generic utility functions.
+ */
+/*! \defgroup group_analysismodules Analysis Modules
+ * \brief
+ * Modules used in analysis tools.
+ */
+/*! \namespace gmx
+ * \brief
+ * Generic Gromacs namespace.
+ *
+ * \inpublicapi
+ */
--- /dev/null
+# includes: Nothing to build, just installation
+install(DIRECTORY . DESTINATION ${INCL_INSTALL_DIR}/gromacs/legacyheaders
+ COMPONENT development
+ PATTERN "Makefile*" EXCLUDE
+ PATTERN "CMake*" EXCLUDE
+ PATTERN "cmake*" EXCLUDE
+)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+#ifndef _GMX_STATS_H
+#define _GMX_STATS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "typedefs.h"
+
+typedef struct gmx_stats *gmx_stats_t;
+
+/* Error codes returned by the routines */
+enum { estatsOK, estatsNO_POINTS, estatsNO_MEMORY, estatsERROR,
+ estatsINVALID_INPUT, estatsNOT_IMPLEMENTED, estatsNR };
+
+enum { elsqWEIGHT_NONE, elsqWEIGHT_X, elsqWEIGHT_Y,
+ elsqWEIGHT_XY, elsqWEIGHT_NR };
+
+enum { ehistoX, ehistoY, ehistoNR };
+
+gmx_stats_t gmx_stats_init();
+
+int gmx_stats_done(gmx_stats_t stats);
+
+/* Remove outliers from a straight line, where level in units of
+ sigma. Level needs to be larger than one obviously. */
+int gmx_stats_remove_outliers(gmx_stats_t stats,double level);
+
+
+
+int gmx_stats_add_point(gmx_stats_t stats,double x,double y,
+ double dx,double dy);
+
+/* The arrays dx and dy may be NULL if no uncertainties are available,
+ in that case zero uncertainties will be assumed. */
+int gmx_stats_add_points(gmx_stats_t stats,int n,real *x,real *y,
+ real *dx,real *dy);
+
+/* Return the data points one by one. Return estatsOK while there are
+ more points, and returns estatsNOPOINTS when the last point has
+ been returned. Should be used in a while loop. Variables for either
+ pointer may be NULL, in which case the routine can be used as an
+ expensive point counter.
+ If level > 0 then the outliers outside level*sigma are reported
+ only. */
+int gmx_stats_get_point(gmx_stats_t stats,real *x,real *y,
+ real *dx,real *dy,real level);
+
+/* Fit the data to y = ax + b, possibly weighted, if uncertainties
+ have been input. Returns slope in *a and intercept in b, *return
+ sigmas in *da and *db respectively. Returns normalized *quality of
+ fit in *chi2 and correlation of fit with data in Rfit. chi2, Rfit,
+ da and db may be NULL. */
+int gmx_stats_get_ab(gmx_stats_t stats,int weight,
+ real *a,real *b,
+ real *da,real *db,real *chi2,real *Rfit);
+
+/* Fit the data to y = ax, possibly weighted, if uncertainties have
+ been input. Returns slope in *a, sigma in a in *da, and normalized
+ quality of fit in *chi2 and correlation of fit with data in
+ Rfit. chi2, Rfit and da may be NULL. */
+int gmx_stats_get_a(gmx_stats_t stats,int weight,
+ real *a,real *da,real *chi2,real *Rfit);
+
+/* Return the correlation coefficient between the data (x and y) as
+ input to the structure. */
+int gmx_stats_get_corr_coeff(gmx_stats_t stats,real *R);
+
+/* Returns the root mean square deviation between x and y values. */
+int gmx_stats_get_rmsd(gmx_stats_t gstats,real *rmsd);
+
+int gmx_stats_get_npoints(gmx_stats_t stats,int *N);
+
+int gmx_stats_get_average(gmx_stats_t stats,real *aver);
+
+int gmx_stats_get_sigma(gmx_stats_t stats,real *sigma);
+
+int gmx_stats_get_error(gmx_stats_t stats,real *error);
+
+/* Get all three of the above. Pointers may be null, in which case no
+ assignment will be done. */
+int gmx_stats_get_ase(gmx_stats_t gstats,real *aver,real *sigma,real *error);
+
+/* Dump the x, y, dx, dy data to a text file */
+int gmx_stats_dump_xy(gmx_stats_t gstats,FILE *fp);
+
+/* Make a histogram of the data present. Uses either bindwith to
+ determine the number of bins, or nbins to determine the binwidth,
+ therefore one of these should be zero, but not the other. If *nbins = 0
+ the number of bins will be returned in this variable. ehisto should be one of
+ ehistoX or ehistoY. If
+ normalized not equal to zero, the integral of the histogram will be
+ normalized to one. The output is in two arrays, *x and *y, to which
+ you should pass a pointer. Memory for the arrays will be allocated
+ as needed. Function returns one of the estats codes. */
+int gmx_stats_make_histogram(gmx_stats_t gstats,real binwidth,int *nbins,
+ int ehisto,
+ int normalized,real **x,real **y);
+
+/* Return message belonging to error code */
+const char *gmx_stats_message(int estats);
+
+/****************************************************
+ * Some statistics utilities for convenience: useful when a complete data
+ * set is available already from another source, e.g. an xvg file.
+ ****************************************************/
+int lsq_y_ax(int n, real x[], real y[], real *a);
+/* Fit a straight line y=ax thru the n data points x, y, return the
+ slope in *a. Return value can be estatsOK, or something else. */
+
+int lsq_y_ax_b(int n, real x[], real y[], real *a, real *b,real *r,
+ real *chi2);
+/* Fit a straight line y=ax+b thru the n data points x,y.
+ * Returns the "fit quality" sigma = sqrt(chi^2/(n-2)).
+ * The correlation coefficient is returned in r.
+ */
+
+int lsq_y_ax_b_xdouble(int n, double x[], real y[],
+ real *a, real *b,real *r,real *chi2);
+/* As lsq_y_ax_b, but with x in double precision.
+ */
+
+int lsq_y_ax_b_error(int n, real x[], real y[], real dy[],
+ real *a, real *b, real *da, real *db,
+ real *r,real *chi2);
+/* Fit a straight line y=ax+b thru the n data points x,y, with sigma dy
+ * Returns the "fit quality" sigma = sqrt(chi^2/(n-2)).
+ * The correlation coefficient is returned in r.
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+
+#ifndef _gmx_wallcycle_h
+#define _gmx_wallcycle_h
+
+#include <stdio.h>
+#include "typedefs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ enum { ewcRUN, ewcSTEP, ewcPPDURINGPME, ewcDOMDEC, ewcDDCOMMLOAD, ewcDDCOMMBOUND, ewcVSITECONSTR, ewcPP_PMESENDX, ewcMOVEX, ewcNS, ewcGB, ewcFORCE, ewcMOVEF, ewcPMEMESH, ewcPME_REDISTXF, ewcPME_SPREADGATHER, ewcPME_FFT, ewcPME_SOLVE, ewcPMEWAITCOMM, ewcPP_PMEWAITRECVF, ewcVSITESPREAD, ewcTRAJ, ewcUPDATE, ewcCONSTR, ewcMoveE, ewcROT, ewcTEST, ewcNR };
+
+gmx_bool wallcycle_have_counter(void);
+/* Returns if cycle counting is supported */
+
+gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr);
+/* Returns the wall cycle structure.
+ * Returns NULL when cycle counting is not supported.
+ */
+
+void wallcycle_start(gmx_wallcycle_t wc, int ewc);
+/* Set the start cycle count for ewc */
+
+double wallcycle_stop(gmx_wallcycle_t wc, int ewc);
+/* Stop the cycle count for ewc, returns the last cycle count */
+
+void wallcycle_reset_all(gmx_wallcycle_t wc);
+/* Resets all cycle counters to zero */
+
+void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc,double cycles[]);
+/* Sum the cycles over the nodes in cr->mpi_comm_mysim */
+
+void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
+ gmx_wallcycle_t wc, double cycles[]);
+/* Print the cycle and time accounting */
+
+gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc);
+/* Return reset_counters from wc struct */
+
+void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters);
+/* Set reset_counters */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _gmx_wallcycle_h */
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+
+#ifndef _gmxfio_h
+#define _gmxfio_h
+
+#include <stdio.h>
+#include "sysstuff.h"
+#include "typedefs.h"
+#include "xdrf.h"
+#include "futil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* types */
+
+
+/* Enumerated for different items in files */
+enum { eitemHEADER, eitemIR, eitemBOX,
+ eitemTOP, eitemX, eitemV, eitemF, eitemNR };
+
+/* Enumerated for data types in files */
+enum { eioREAL, eioFLOAT, eioDOUBLE, eioINT, eioGMX_LARGE_INT,
+ eioUCHAR, eioNUCHAR, eioUSHORT,
+ eioRVEC, eioNRVEC, eioIVEC, eioSTRING, eioNR };
+
+typedef struct t_fileio t_fileio;
+
+extern const char *itemstr[eitemNR];
+extern const char *comment_str[eitemNR];
+
+/* NOTE ABOUT THREAD SAFETY:
+
+ The functions are all thread-safe, provided that two threads don't
+ do something silly like closing the same file, or one thread
+ accesses a file that has been closed by another.
+ */
+
+/********************************************************
+ * Open and Close
+ ********************************************************/
+
+t_fileio *gmx_fio_open(const char *fn,const char *mode);
+/* Open a new file for reading or writing.
+ * The file type will be deduced from the file name.
+ * If fn is NULL, stdin / stdout will be used for Ascii I/O (TPA type)
+ * mode may be "r", "w", or "a". You should append a "b" to the mode
+ * if you are writing a binary file, but the routine will also
+ * doublecheck it and try to do it if you forgot. This has no effect on
+ * unix, but is important on windows.
+ */
+
+int gmx_fio_close(t_fileio *fp);
+/* Close the file corresponding to fp (if not stdio)
+ * The routine will exit when an invalid fio is handled.
+ * Returns 0 on success.
+ */
+
+int gmx_fio_fp_close(t_fileio *fp);
+/* Close the file corresponding to fp without closing the FIO entry
+ * Needed e.g. for trxio because the FIO entries are used to store
+ * additional data.
+ * NOTE that the fp still needs to be properly closed with gmx_fio_close().
+ * The routine will exit when an invalid fio is handled.
+ * Returns 0 on success.
+ */
+
+
+/* Open a file, return a stream, record the entry in internal FIO object */
+FILE* gmx_fio_fopen(const char *fn,const char *mode);
+
+/* Close a file previously opened with gmx_fio_fopen.
+ * Do not mix these calls with standard fopen/fclose ones!
+ * Returns 0 on success. */
+int gmx_fio_fclose(FILE *fp);
+
+
+
+/********************************************************
+ * Change properties of the open file
+ ********************************************************/
+
+void gmx_fio_setprecision(t_fileio *fio,gmx_bool bDouble);
+/* Select the floating point precision for reading and writing files */
+
+char *gmx_fio_getname(t_fileio *fio);
+/* Return the filename corresponding to the fio index */
+
+int gmx_fio_getftp(t_fileio *fio);
+/* Return the filetype corresponding to the fio index.
+ There is as of now no corresponding setftp function because the file
+ was opened as a specific file type and changing that midway is most
+ likely an evil hack. */
+
+void gmx_fio_setdebug(t_fileio *fio,gmx_bool bDebug);
+/* Set the debug mode */
+
+gmx_bool gmx_fio_getdebug(t_fileio *fio);
+/* Return whether debug mode is on in fio */
+
+gmx_bool gmx_fio_getread(t_fileio *fio);
+/* Return whether read mode is on in fio */
+
+
+void gmx_fio_checktype(t_fileio *fio);
+/* Check whether the fio is of a sane type */
+
+/***************************************************
+ * FILE Operations
+ ***************************************************/
+
+void gmx_fio_rewind(t_fileio *fio);
+/* Rewind the tpa file in fio */
+
+int gmx_fio_flush(t_fileio *fio);
+/* Flush the fio, returns 0 on success */
+
+int gmx_fio_fsync(t_fileio *fio);
+/* fsync the fio, returns 0 on success.
+ NOTE: don't use fsync function unless you're absolutely sure you need it
+ because it deliberately interferes with the OS's caching mechanisms and
+ can cause dramatically slowed down IO performance. Some OSes (Linux,
+ for example), may implement fsync as a full sync() point. */
+
+gmx_off_t gmx_fio_ftell(t_fileio *fio);
+/* Return file position if possible */
+
+int gmx_fio_seek(t_fileio *fio,gmx_off_t fpos);
+/* Set file position if possible, quit otherwise */
+
+FILE *gmx_fio_getfp(t_fileio *fio);
+/* Return the file pointer itself */
+
+XDR *gmx_fio_getxdr(t_fileio *fio);
+/* Return the file pointer itself */
+
+
+
+
+
+/* Element with information about position in a currently open file.
+ * gmx_off_t should be defined by autoconf if your system does not have it.
+ * If you do not have it on some other platform you do not have largefile
+ * support at all, and you can define it to int (or better, find out how to
+ * enable large files). */
+typedef struct
+{
+ char filename[STRLEN];
+ gmx_off_t offset;
+ unsigned char chksum[16];
+ int chksum_size;
+}
+gmx_file_position_t;
+
+
+int gmx_fio_check_file_position(t_fileio *fio);
+/* Check if the file position is out of the range of off_t.
+ * The result is stored along with the other file data of fio.
+ */
+
+int gmx_fio_get_output_file_positions(gmx_file_position_t ** outputfiles,
+ int *nfiles );
+/* Return the name and file pointer positions for all currently open
+ * output files. This is used for saving in the checkpoint files, so we
+ * can truncate output files upon restart-with-appending.
+ *
+ * For the first argument you should use a pointer, which will be set to
+ * point to a list of open files.
+ */
+
+t_fileio *gmx_fio_all_output_fsync(void);
+/* fsync all open output files. This is used for checkpointing, where
+ we need to ensure that all output is actually written out to
+ disk.
+ This is most important in the case of some networked file systems,
+ where data is not synced with the file server until close() or
+ fsync(), so data could remain in cache for days.
+ Note the caveats reported with gmx_fio_fsync().
+
+ returns: NULL if no error occurred, or a pointer to the first file that
+ failed if an error occurred
+*/
+
+
+int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
+ unsigned char digest[]);
+
+
+int xtc_seek_frame(t_fileio *fio, int frame, int natoms);
+
+int xtc_seek_time(t_fileio *fio, real time, int natoms);
+
+
+/* Add this to the comment string for debugging */
+void gmx_fio_set_comment(t_fileio *fio, const char *comment);
+
+/* Remove previously set comment */
+void gmx_fio_unset_comment(t_fileio *fio);
+
+
+
+
+/********************************************************
+ * Read and write
+ ********************************************************/
+
+
+/* basic reading & writing */
+gmx_bool gmx_fio_reade_real(t_fileio *fio, real *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_float(t_fileio *fio, float *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_double(t_fileio *fio, double *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_int(t_fileio *fio, int *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_gmx_large_int(t_fileio *fio, gmx_large_int_t *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_uchar(t_fileio *fio, unsigned char *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_ushort(t_fileio *fio, unsigned short *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_rvec(t_fileio *fio, rvec *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_ivec(t_fileio *fio, ivec *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_reade_string(t_fileio *fio, char *item,
+ const char *desc, const char *srcfile, int line);
+
+gmx_bool gmx_fio_writee_real(t_fileio *fio, real item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_float(t_fileio *fio, float item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_double(t_fileio *fio, double item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_int(t_fileio *fio, int item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_gmx_large_int(t_fileio *fio, gmx_large_int_t item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_uchar(t_fileio *fio, unsigned char item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_ushort(t_fileio *fio, unsigned short item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_rvec(t_fileio *fio, rvec *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_ivec(t_fileio *fio, ivec *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_writee_string(t_fileio *fio, const char *item,
+ const char *desc, const char *srcfile, int line);
+
+/* reading or writing, depending on the file's opening mode string */
+gmx_bool gmx_fio_doe_real(t_fileio *fio, real *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_float(t_fileio *fio, float *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_double(t_fileio *fio, double *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_gmx_bool(t_fileio *fio, gmx_bool *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_int(t_fileio *fio, int *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_gmx_large_int(t_fileio *fio, gmx_large_int_t *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_uchar(t_fileio *fio, unsigned char *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_ushort(t_fileio *fio, unsigned short *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_rvec(t_fileio *fio, rvec *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_ivec(t_fileio *fio, ivec *item,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_doe_string(t_fileio *fio, char *item,
+ const char *desc, const char *srcfile, int line);
+
+
+
+
+/* array reading & writing */
+gmx_bool gmx_fio_nreade_real(t_fileio *fio, real *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_float(t_fileio *fio, float *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_double(t_fileio *fio, double *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_int(t_fileio *fio, int *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, int n,
+ const char *desc, const char *srcfile,
+ int line);
+gmx_bool gmx_fio_nreade_uchar(t_fileio *fio, unsigned char *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_ushort(t_fileio *fio, unsigned short *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_rvec(t_fileio *fio, rvec *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_ivec(t_fileio *fio, ivec *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nreade_string(t_fileio *fio, char *item[], int n,
+ const char *desc, const char *srcfile, int line);
+
+gmx_bool gmx_fio_nwritee_real(t_fileio *fio, const real *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_float(t_fileio *fio, const float *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_double(t_fileio *fio, const double *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_int(t_fileio *fio, const int *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_gmx_large_int(t_fileio *fio,
+ const gmx_large_int_t *item, int n,
+ const char *desc, const char *srcfile,
+ int line);
+gmx_bool gmx_fio_nwritee_uchar(t_fileio *fio, const unsigned char *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_ushort(t_fileio *fio, const unsigned short *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_rvec(t_fileio *fio, const rvec *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_ivec(t_fileio *fio, const ivec *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_nwritee_string(t_fileio *fio, const char *item[], int n,
+ const char *desc, const char *srcfile, int line);
+
+gmx_bool gmx_fio_ndoe_real(t_fileio *fio, real *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_float(t_fileio *fio, float *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_double(t_fileio *fio, double *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_gmx_bool(t_fileio *fio, gmx_bool *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_int(t_fileio *fio, int *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, int n,
+ const char *desc, const char *srcfile,
+ int line);
+gmx_bool gmx_fio_ndoe_uchar(t_fileio *fio, unsigned char *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_ushort(t_fileio *fio, unsigned short *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_rvec(t_fileio *fio, rvec *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_ivec(t_fileio *fio, ivec *item, int n,
+ const char *desc, const char *srcfile, int line);
+gmx_bool gmx_fio_ndoe_string(t_fileio *fio, char *item[], int n,
+ const char *desc, const char *srcfile, int line);
+
+
+
+/* convenience macros */
+#define gmx_fio_read_real(fio, item) gmx_fio_reade_real(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_float(fio, item) gmx_fio_reade_float(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_double(fio, item) gmx_fio_reade_double(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_int(fio, item) gmx_fio_reade_int(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_gmx_large_int(fio, item) gmx_fio_reade_gmx_large_int(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_uchar(fio, item) gmx_fio_reade_uchar(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_ushort(fio, item) gmx_fio_reade_ushort(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_rvec(fio, item) gmx_fio_reade_rvec(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_ivec(fio, item) gmx_fio_reade_ivec(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_read_string(fio, item) gmx_fio_reade_string(fio, item, (#item), __FILE__, __LINE__)
+
+#define gmx_fio_write_real(fio, item) gmx_fio_writee_real(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_float(fio, item) gmx_fio_writee_float(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_double(fio, item) gmx_fio_writee_double(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_int(fio, item) gmx_fio_writee_int(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_gmx_large_int(fio, item) gmx_fio_writee_gmx_large_int(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_uchar(fio, item) gmx_fio_writee_uchar(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_ushort(fio, item) gmx_fio_writee_ushort(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_rvec(fio, item) gmx_fio_writee_rvec(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_ivec(fio, item) gmx_fio_writee_ivec(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_write_string(fio, item) gmx_fio_writee_string(fio, item, (#item), __FILE__, __LINE__)
+
+#define gmx_fio_do_real(fio, item) gmx_fio_doe_real(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_float(fio, item) gmx_fio_doe_float(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_double(fio, item) gmx_fio_doe_double(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_gmx_bool(fio, item) gmx_fio_doe_gmx_bool(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_int(fio, item) gmx_fio_doe_int(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_gmx_large_int(fio, item) gmx_fio_doe_gmx_large_int(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_uchar(fio, item) gmx_fio_doe_uchar(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_ushort(fio, item) gmx_fio_doe_ushort(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_rvec(fio, item) gmx_fio_doe_rvec(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_ivec(fio, item) gmx_fio_doe_ivec(fio, &item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_string(fio, item) gmx_fio_doe_string(fio, item, (#item), __FILE__, __LINE__)
+
+
+
+
+#define gmx_fio_nread_real(fio, item, n) gmx_fio_nreade_real(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_float(fio, item, n) gmx_fio_nreade_float(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_double(fio, item, n) gmx_fio_nreade_double(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_int(fio, item, n) gmx_fio_nreade_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_gmx_large_int(fio, item, n) gmx_fio_nreade_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_uchar(fio, item, n) gmx_fio_nreade_uchar(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_ushort(fio, item, n) gmx_fio_nreade_ushort(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_rvec(fio, item, n) gmx_fio_nreade_rvec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_ivec(fio, item, n) gmx_fio_nreade_ivec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nread_string(fio, item, n) gmx_fio_nreade_string(fio, item, n, (#item), __FILE__, __LINE__)
+
+#define gmx_fio_nwrite_real(fio, item, n) gmx_fio_nwritee_real(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_float(fio, item, n) gmx_fio_nwritee_float(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_double(fio, item, n) gmx_fio_nwritee_double(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_int(fio, item, n) gmx_fio_nwritee_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_gmx_large_int(fio, item, n) gmx_fio_nwritee_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_uchar(fio, item, n) gmx_fio_nwritee_uchar(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_ushort(fio, item, n) gmx_fio_nwritee_ushort(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_rvec(fio, item, n) gmx_fio_nwritee_rvec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_ivec(fio, item, n) gmx_fio_nwritee_ivec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_nwrite_string(fio, item, n) gmx_fio_nwritee_string(fio, item, n, (#item), __FILE__, __LINE__)
+
+#define gmx_fio_ndo_real(fio, item, n) gmx_fio_ndoe_real(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_float(fio, item, n) gmx_fio_ndoe_float(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_double(fio, item, n) gmx_fio_ndoe_double(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_gmx_bool(fio, item, n) gmx_fio_ndoe_gmx_bool(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_int(fio, item, n) gmx_fio_ndoe_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_gmx_large_int(fio, item, n) gmx_fio_ndoe_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_uchar(fio, item, n) gmx_fio_ndoe_uchar(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_ushort(fio, item, n) gmx_fio_ndoe_ushort(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_rvec(fio, item, n) gmx_fio_ndoe_rvec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_ivec(fio, item, n) gmx_fio_ndoe_ivec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_string(fio, item, n) gmx_fio_ndoe_string(fio, item, n, (#item), __FILE__, __LINE__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+
+#ifndef _mdrun_h
+#define _mdrun_h
+
+#include <stdio.h>
+#include <time.h>
+#include "typedefs.h"
+#include "network.h"
+#include "tgroup.h"
+#include "filenm.h"
+#include "mshift.h"
+#include "force.h"
+#include "edsam.h"
+#include "mdebin.h"
+#include "vcm.h"
+#include "vsite.h"
+#include "pull.h"
+#include "update.h"
+#include "membed.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MD_POLARISE (1<<2)
+#define MD_IONIZE (1<<3)
+#define MD_RERUN (1<<4)
+#define MD_RERUN_VSITE (1<<5)
+#define MD_FFSCAN (1<<6)
+#define MD_SEPPOT (1<<7)
+#define MD_PARTDEC (1<<9)
+#define MD_DDBONDCHECK (1<<10)
+#define MD_DDBONDCOMM (1<<11)
+#define MD_CONFOUT (1<<12)
+#define MD_REPRODUCIBLE (1<<13)
+#define MD_READ_RNG (1<<14)
+#define MD_APPENDFILES (1<<15)
+#define MD_KEEPANDNUMCPT (1<<16)
+#define MD_READ_EKIN (1<<17)
+#define MD_STARTFROMCPT (1<<18)
+#define MD_RESETCOUNTERSHALFWAY (1<<19)
+
+/* Define a number of flags to better control the information
+ * passed to compute_globals in md.c and global_stat.
+ */
+
+/* We are rerunning the simulation */
+#define CGLO_RERUNMD (1<<1)
+/* we are computing the kinetic energy from average velocities */
+#define CGLO_EKINAVEVEL (1<<2)
+/* we are removing the center of mass momenta */
+#define CGLO_STOPCM (1<<3)
+/* bGStat is defined in do_md */
+#define CGLO_GSTAT (1<<4)
+/* Sum the energy terms in global computation */
+#define CGLO_ENERGY (1<<6)
+/* Sum the kinetic energy terms in global computation */
+#define CGLO_TEMPERATURE (1<<7)
+/* Sum the kinetic energy terms in global computation */
+#define CGLO_PRESSURE (1<<8)
+/* Sum the constraint term in global computation */
+#define CGLO_CONSTRAINT (1<<9)
+/* we are using an integrator that requires iteration over some steps - currently not used*/
+#define CGLO_ITERATE (1<<10)
+/* it is the first time we are iterating (or, only once through is required */
+#define CGLO_FIRSTITERATE (1<<11)
+/* Reading ekin from the trajectory */
+#define CGLO_READEKIN (1<<12)
+/* we need to reset the ekin rescaling factor here */
+#define CGLO_SCALEEKIN (1<<13)
+
+enum {
+ ddnoSEL, ddnoINTERLEAVE, ddnoPP_PME, ddnoCARTESIAN, ddnoNR
+};
+
+typedef struct {
+ double real;
+#ifdef GMX_CRAY_XT3
+ double proc;
+#else
+ clock_t proc;
+#endif
+ double realtime;
+ double proctime;
+ double time_per_step;
+ double last;
+ gmx_large_int_t nsteps_done;
+} gmx_runtime_t;
+
+typedef struct {
+ t_fileio *fp_trn;
+ t_fileio *fp_xtc;
+ int xtc_prec;
+ ener_file_t fp_ene;
+ const char *fn_cpt;
+ gmx_bool bKeepAndNumCPT;
+ int eIntegrator;
+ int simulation_part;
+ FILE *fp_dhdl;
+ FILE *fp_field;
+} gmx_mdoutf_t;
+
+/* Variables for temporary use with the deform option,
+ * used in runner.c and md.c.
+ * (These variables should be stored in the tpx file.)
+ */
+extern gmx_large_int_t deform_init_init_step_tpx;
+extern matrix deform_init_box_tpx;
+#ifdef GMX_THREADS
+extern tMPI_Thread_mutex_t deform_init_box_mutex;
+
+/* The minimum number of atoms per thread. With fewer atoms than this,
+ * the number of threads will get lowered.
+ */
+#define MIN_ATOMS_PER_THREAD 90
+#endif
+
+
+typedef double gmx_integrator_t(FILE *log,t_commrec *cr,
+ int nfile,const t_filenm fnm[],
+ const output_env_t oenv, gmx_bool bVerbose,
+ gmx_bool bCompact, int nstglobalcomm,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int stepout,
+ t_inputrec *inputrec,
+ gmx_mtop_t *mtop,t_fcdata *fcd,
+ t_state *state,
+ t_mdatoms *mdatoms,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_edsam_t ed,
+ t_forcerec *fr,
+ int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime);
+
+typedef struct gmx_global_stat *gmx_global_stat_t;
+
+/* ROUTINES from md.c */
+
+gmx_integrator_t do_md;
+
+gmx_integrator_t do_md_openmm;
+
+/* ROUTINES from minimize.c */
+
+gmx_integrator_t do_steep;
+/* Do steepest descents EM */
+
+gmx_integrator_t do_cg;
+/* Do conjugate gradient EM */
+
+gmx_integrator_t do_lbfgs;
+/* Do conjugate gradient L-BFGS */
+
+gmx_integrator_t do_nm;
+/* Do normal mode analysis */
+
+/* ROUTINES from tpi.c */
+
+gmx_integrator_t do_tpi;
+/* Do test particle insertion */
+
+
+/* ROUTINES from sim_util.c */
+void do_pbc_first(FILE *log,matrix box,t_forcerec *fr,
+ t_graph *graph,rvec x[]);
+
+void do_pbc_first_mtop(FILE *fplog,int ePBC,matrix box,
+ gmx_mtop_t *mtop,rvec x[]);
+
+void do_pbc_mtop(FILE *fplog,int ePBC,matrix box,
+ gmx_mtop_t *mtop,rvec x[]);
+
+
+/* ROUTINES from stat.c */
+gmx_global_stat_t global_stat_init(t_inputrec *ir);
+
+void global_stat_destroy(gmx_global_stat_t gs);
+
+void global_stat(FILE *log,gmx_global_stat_t gs,
+ t_commrec *cr,gmx_enerdata_t *enerd,
+ tensor fvir,tensor svir,rvec mu_tot,
+ t_inputrec *inputrec,
+ gmx_ekindata_t *ekind,
+ gmx_constr_t constr,t_vcm *vcm,
+ int nsig,real *sig,
+ gmx_mtop_t *top_global, t_state *state_local,
+ gmx_bool bSumEkinhOld, int flags);
+/* Communicate statistics over cr->mpi_comm_mysim */
+
+gmx_mdoutf_t *init_mdoutf(int nfile,const t_filenm fnm[],
+ int mdrun_flags,
+ const t_commrec *cr,const t_inputrec *ir,
+ const output_env_t oenv);
+/* Returns a pointer to a data structure with all output file pointers
+ * and names required by mdrun.
+ */
+
+void done_mdoutf(gmx_mdoutf_t *of);
+/* Close all open output files and free the of pointer */
+
+#define MDOF_X (1<<0)
+#define MDOF_V (1<<1)
+#define MDOF_F (1<<2)
+#define MDOF_XTC (1<<3)
+#define MDOF_CPT (1<<4)
+
+void write_traj(FILE *fplog,t_commrec *cr,
+ gmx_mdoutf_t *of,
+ int mdof_flags,
+ gmx_mtop_t *top_global,
+ gmx_large_int_t step,double t,
+ t_state *state_local,t_state *state_global,
+ rvec *f_local,rvec *f_global,
+ int *n_xtc,rvec **x_xtc);
+/* Routine that writes frames to trn, xtc and/or checkpoint.
+ * What is written is determined by the mdof_flags defined above.
+ * Data is collected to the master node only when necessary.
+ */
+
+int do_per_step(gmx_large_int_t step,gmx_large_int_t nstep);
+/* Return TRUE if io should be done */
+
+int do_any_io(int step, t_inputrec *ir);
+
+/* ROUTINES from sim_util.c */
+
+double gmx_gettime();
+
+void print_time(FILE *out, gmx_runtime_t *runtime,
+ gmx_large_int_t step,t_inputrec *ir, t_commrec *cr);
+
+void runtime_start(gmx_runtime_t *runtime);
+
+void runtime_end(gmx_runtime_t *runtime);
+
+void runtime_upd_proc(gmx_runtime_t *runtime);
+/* The processor time should be updated every once in a while,
+ * since on 32-bit manchines it loops after 72 minutes.
+ */
+
+void print_date_and_time(FILE *log,int pid,const char *title,
+ const gmx_runtime_t *runtime);
+
+void nstop_cm(FILE *log,t_commrec *cr,
+ int start,int nr_atoms,real mass[],rvec x[],rvec v[]);
+
+void finish_run(FILE *log,t_commrec *cr,const char *confout,
+ t_inputrec *inputrec,
+ t_nrnb nrnb[],gmx_wallcycle_t wcycle,
+ gmx_runtime_t *runtime,
+ gmx_bool bWriteStat);
+
+void calc_enervirdiff(FILE *fplog,int eDispCorr,t_forcerec *fr);
+
+void calc_dispcorr(FILE *fplog,t_inputrec *ir,t_forcerec *fr,
+ gmx_large_int_t step, int natoms,
+ matrix box,real lambda,tensor pres,tensor virial,
+ real *prescorr, real *enercorr, real *dvdlcorr);
+
+typedef enum
+{
+ LIST_SCALARS =0001,
+ LIST_INPUTREC =0002,
+ LIST_TOP =0004,
+ LIST_X =0010,
+ LIST_V =0020,
+ LIST_F =0040,
+ LIST_LOAD =0100
+} t_listitem;
+
+void check_nnodes_top(char *fn,t_topology *top);
+/* Reset the tpr file to work with one node if necessary */
+
+
+/* check the version */
+void check_ir_old_tpx_versions(t_commrec *cr,FILE *fplog,
+ t_inputrec *ir,gmx_mtop_t *mtop);
+
+/* Allocate and initialize node-local state entries. */
+void set_state_entries(t_state *state,const t_inputrec *ir,int nnodes);
+
+/* Broadcast the data for a simulation, and allocate node-specific settings
+ such as rng generators. */
+void init_parallel(FILE *log, t_commrec *cr, t_inputrec *inputrec,
+ gmx_mtop_t *mtop);
+
+
+void do_constrain_first(FILE *log,gmx_constr_t constr,
+ t_inputrec *inputrec,t_mdatoms *md,
+ t_state *state,rvec *f,
+ t_graph *graph,t_commrec *cr,t_nrnb *nrnb,
+ t_forcerec *fr, gmx_localtop_t *top, tensor shake_vir);
+
+void dynamic_load_balancing(gmx_bool bVerbose,t_commrec *cr,real capacity[],
+ int dimension,t_mdatoms *md,t_topology *top,
+ rvec x[],rvec v[],matrix box);
+/* Perform load balancing, i.e. split the particles over processors
+ * based on their coordinates in the "dimension" direction.
+ */
+
+int mdrunner(int nthreads_requested, FILE *fplog,t_commrec *cr,int nfile,
+ const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
+ gmx_bool bCompact, int nstglobalcomm, ivec ddxyz,int dd_node_order,
+ real rdd, real rconstr, const char *dddlb_opt,real dlb_scale,
+ const char *ddcsx,const char *ddcsy,const char *ddcsz,
+ int nstepout, int resetstep, int nmultisim, int repl_ex_nst,
+ int repl_ex_seed, real pforce,real cpt_period,real max_hours,
+ const char *deviceOptions, unsigned long Flags);
+/* Driver routine, that calls the different methods */
+
+void md_print_warning(const t_commrec *cr,FILE *fplog,const char *buf);
+/* Print a warning message to stderr on the master node
+ * and to fplog if fplog!=NULL.
+ */
+
+void init_md(FILE *fplog,
+ t_commrec *cr,t_inputrec *ir, const output_env_t oenv,
+ double *t,double *t0,
+ real *lambda,double *lam0,
+ t_nrnb *nrnb,gmx_mtop_t *mtop,
+ gmx_update_t *upd,
+ int nfile,const t_filenm fnm[],
+ gmx_mdoutf_t **outf,t_mdebin **mdebin,
+ tensor force_vir,tensor shake_vir,
+ rvec mu_tot,
+ gmx_bool *bSimAnn,t_vcm **vcm,
+ t_state *state, unsigned long Flags);
+ /* Routine in sim_util.c */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _mdrun_h */
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+
+#ifndef _gmx_membed_h
+#define _gmx_membed_h
+
+#include "typedefs.h"
+
+/* information about scaling center */
+typedef struct {
+ rvec xmin;
+ rvec xmax;
+ rvec *geom_cent;
+ int pieces;
+ int *nidx;
+ atom_id **subindex;
+} pos_ins_t;
+
+/* variables needed in do_md */
+typedef struct {
+ int it_xy;
+ int it_z;
+ real xy_step;
+ real z_step;
+ rvec fac; /* scaling factor */
+ rvec *r_ins; /* target coordinates */
+ pos_ins_t *pos_ins;
+} gmx_membed_t;
+
+/* initialisation of membed code */
+extern void init_membed(FILE *fplog, gmx_membed_t *membed, int nfile, const t_filenm fnm[],
+ gmx_mtop_t *mtop, t_inputrec *inputrec, t_state *state, t_commrec *cr,
+ real *cpt);
+
+/* rescaling the coordinates voor de membed code */
+extern void rescale_membed(int step_rel, gmx_membed_t *membed, rvec *x);
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* define USE_MPE if you want MPE logging
+ *
+ * you then need to link with the appropriate libraries
+ * that come with the mpich distribution (can be found in the
+ * mpe subdirectory */
+/* #define USE_MPE */
+
+/* define BARRIERS if you want to have extra MPI_Barriers
+ * in the code which might help analyzing the MPE logfiles
+ */
+/* #define BARRIERS */
+#ifdef BARRIERS
+#define GMX_BARRIER(communicator) MPI_Barrier(communicator)
+#else
+#define GMX_BARRIER(communicator)
+#endif
+
+#ifdef USE_MPE
+#define GMX_MPE_LOG(event) MPE_Log_event(event, 0, "")
+#else
+#define GMX_MPE_LOG(event)
+#endif
+
+#ifdef USE_MPE
+#include <mpe.h>
+ /* Define MPE logging events here */
+ /* General events */
+ int ev_timestep1, ev_timestep2;
+ int ev_ns_start, ev_ns_finish;
+ int ev_calc_bonds_start, ev_calc_bonds_finish;
+ int ev_send_coordinates_start, ev_send_coordinates_finish;
+ int ev_update_fr_start, ev_update_fr_finish;
+ int ev_clear_rvecs_start, ev_clear_rvecs_finish;
+ int ev_output_start, ev_output_finish;
+ int ev_update_start, ev_update_finish;
+ int ev_force_start, ev_force_finish;
+ int ev_do_fnbf_start, ev_do_fnbf_finish;
+ int ev_test_start, ev_test_finish;
+
+ /* COM and enforced rotation pulling */
+ int ev_flexll_start, ev_flexll_finish;
+ int ev_add_rot_forces_start, ev_add_rot_forces_finish;
+ int ev_forcecycles_start, ev_forcecycles_finish;
+ int ev_rotcycles_start, ev_rotcycles_finish;
+
+ /* Shift related events*/
+ int ev_shift_start, ev_shift_finish;
+ int ev_unshift_start, ev_unshift_finish;
+ int ev_mk_mshift_start, ev_mk_mshift_finish;
+
+ /* PME related events */
+ int ev_pme_start, ev_pme_finish;
+ int ev_spread_on_grid_start, ev_spread_on_grid_finish;
+ int ev_sum_qgrid_start, ev_sum_qgrid_finish;
+ int ev_gmxfft3d_start, ev_gmxfft3d_finish;
+ int ev_solve_pme_start, ev_solve_pme_finish;
+ int ev_gather_f_bsplines_start, ev_gather_f_bsplines_finish;
+ int ev_reduce_start, ev_reduce_finish;
+ int ev_rscatter_start, ev_rscatter_finish;
+ int ev_alltoall_start, ev_alltoall_finish;
+ int ev_pmeredist_start, ev_pmeredist_finish;
+ int ev_init_pme_start, ev_init_pme_finish;
+ int ev_global_stat_start, ev_global_stat_finish;
+ int ev_sum_lrforces_start, ev_sum_lrforces_finish;
+ int ev_virial_start, ev_virial_finish;
+ int ev_sort_start, ev_sort_finish;
+ int ev_sum_qgrid_start, ev_sum_qgrid_finish;
+
+ /* Essential dynamics related events */
+ int ev_edsam_start, ev_edsam_finish;
+ int ev_get_group_x_start, ev_get_group_x_finish;
+ int ev_ed_apply_cons_start, ev_ed_apply_cons_finish;
+ int ev_fit_to_reference_start, ev_fit_to_reference_finish;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+
+#ifndef _names_h
+#define _names_h
+
+
+#include "typedefs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* All string arrays are NULL terminated, and therefore have an
+ * extra argument (the +1)
+ * these should correspond to names.c and include/types/enums.h
+ */
+extern const char *epbc_names[epbcNR+1];
+extern const char *etcoupl_names[etcNR+1];
+extern const char *epcoupl_names[epcNR+1];
+extern const char *epcoupltype_names[epctNR+1];
+extern const char *erefscaling_names[erscNR+1];
+extern const char *ens_names[ensNR+1];
+extern const char *ei_names[eiNR+1];
+extern const char *yesno_names[BOOL_NR+1];
+extern const char *bool_names[BOOL_NR+1];
+extern const char *eel_names[eelNR+1];
+extern const char *eewg_names[eewgNR+1];
+extern const char *evdw_names[evdwNR+1];
+extern const char *econstr_names[econtNR+1];
+extern const char *ptype_str[eptNR+1];
+extern const char *egrp_nm[egNR+1];
+extern const char *edisre_names[edrNR+1];
+extern const char *edisreweighting_names[edrwNR+1];
+extern const char *enbf_names[eNBF_NR+1];
+extern const char *ecomb_names[eCOMB_NR+1];
+extern const char *gtypes[egcNR+1];
+extern const char *efep_names[efepNR+1];
+extern const char *separate_dhdl_file_names[efepNR+1];
+extern const char *dhdl_derivatives_names[efepNR+1];
+extern const char *efep_names[efepNR+1];
+extern const char *esol_names[esolNR+1];
+extern const char *enlist_names[enlistNR+1];
+extern const char *edispc_names[edispcNR+1];
+extern const char *ecm_names[ecmNR+1];
+extern const char *eann_names[eannNR+1];
+extern const char *egb_names[egbNR+1];
+extern const char *eis_names[eisNR+1];
+extern const char *esa_names[esaNR+1];
+extern const char *ewt_names[ewtNR+1];
+extern const char *epull_names[epullNR+1];
+extern const char *epullg_names[epullgNR+1];
+extern const char *erotg_names[erotgNR+1];
+extern const char *erotg_originnames[erotgNR+1];
+extern const char *erotg_fitnames[erotgFitNR+1];
+extern const char *eQMmethod_names[eQMmethodNR+1];
+extern const char *eQMbasis_names[eQMbasisNR+1];
+extern const char *eQMMMscheme_names[eQMMMschemeNR+1];
+extern const char *eMultentOpt_names[eMultentOptNR+1];
+
+#define UNDEFINED "UNDEFINED"
+#define ENUM_NAME(e,max,names) ((((e)<0)||((e)>=(max)))?UNDEFINED:(names)[e])
+
+#define BOOL(e) ENUM_NAME(e,BOOL_NR,bool_names)
+#define ENS(e) ENUM_NAME(e,ensNR,ens_names)
+#define EI(e) ENUM_NAME(e,eiNR,ei_names)
+#define EPBC(e) ENUM_NAME(e,epbcNR,epbc_names)
+#define ETCOUPLTYPE(e) ENUM_NAME(e,etcNR,etcoupl_names)
+#define EPCOUPLTYPE(e) ENUM_NAME(e,epcNR,epcoupl_names)
+#define EPCOUPLTYPETYPE(e) ENUM_NAME(e,epctNR,epcoupltype_names)
+#define EREFSCALINGTYPE(e) ENUM_NAME(e,erscNR,erefscaling_names)
+#define EBLOCKS(e) ENUM_NAME(e,ebNR,eblock_names)
+#define EPARAM(e) ENUM_NAME(e,epNR,eparam_names)
+#define EELTYPE(e) ENUM_NAME(e,eelNR,eel_names)
+#define EVDWTYPE(e) ENUM_NAME(e,evdwNR,evdw_names)
+#define ECONSTRTYPE(e) ENUM_NAME(e,econtNR,econstr_names)
+#define EDISRETYPE(e) ENUM_NAME(e,edrNR,edisre_names)
+#define EDISREWEIGHTING(e) ENUM_NAME(e,edrwNR,edisreweighting_names)
+#define ENBFNAME(e) ENUM_NAME(e,eNBF_NR,enbf_names)
+#define ECOMBNAME(e) ENUM_NAME(e,eCOMB_NR,ecomb_names)
+#define EFEPTYPE(e) ENUM_NAME(e,efepNR,efep_names)
+#define SEPDHDLFILETYPE(e) ENUM_NAME(e,sepdhdlfileNR,separate_dhdl_file_names)
+#define DHDLDERIVATIVESTYPE(e) ENUM_NAME(e,dhdlderivativesNR,dhdl_derivatives_names)
+#define ESOLTYPE(e) ENUM_NAME(e,esolNR,esol_names)
+#define ENLISTTYPE(e) ENUM_NAME(e,enlistNR,enlist_names)
+#define EDISPCORR(e) ENUM_NAME(e,edispcNR,edispc_names)
+#define ECOM(e) ENUM_NAME(e,ecmNR,ecm_names)
+#define EANNEAL(e) ENUM_NAME(e,eannNR,eann_names)
+#define EGBALGORITHM(e) ENUM_NAME(e,egbNR,egb_names)
+#define ESAALGORITHM(e) ENUM_NAME(e,esaNR,esa_names)
+#define EIMPLICITSOL(e) ENUM_NAME(e,eisNR,eis_names)
+#define EWALLTYPE(e) ENUM_NAME(e,ewtNR,ewt_names)
+#define EPULLTYPE(e) ENUM_NAME(e,epullNR,epull_names)
+#define EPULLGEOM(e) ENUM_NAME(e,epullgNR,epullg_names)
+#define EROTGEOM(e) ENUM_NAME(e,erotgNR,erotg_names)
+#define EROTORIGIN(e) ENUM_NAME(e,erotgOriginNR,erotg_originnames)
+#define EROTFIT(e) ENUM_NAME(e,erotgFitNR,erotg_fitnames)
+#define EQMMETHOD(e) ENUM_NAME(e,eQMmethodNR,eQMmethod_names)
+#define EQMBASIS(e) ENUM_NAME(e,eQMbasisNR,eQMbasis_names)
+#define EQMMMSCHEME(e) ENUM_NAME(e,eQMMMschemeNR,eQMMMscheme_names)
+#define EMULTENTOPT(e) ENUM_NAME(e,eMultentOptNR,eMultentOpt_names)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _names_h */
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+
+#ifndef _physics_h
+#define _physics_h
+
+/*
+ * Physical constants to be used in Gromacs.
+ * No constants (apart from 0, 1 or 2) should
+ * be anywhere else in the code.
+ */
+
+#include <math.h>
+
+/* we do it anyway. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef M_PI
+#ifdef _PI
+#define M_PI _PI
+#else
+#define M_PI 3.14159265358979323846
+#endif
+#endif
+
+#define ANGSTROM (1e-10) /* Old... */
+#define KILO (1e3) /* Thousand */
+#define NANO (1e-9) /* A Number */
+#define PICO (1e-12) /* A Number */
+#define A2NM (ANGSTROM/NANO) /* NANO */
+#define NM2A (NANO/ANGSTROM) /* 10.0 */
+#define RAD2DEG (180.0/M_PI) /* Conversion */
+#define DEG2RAD (M_PI/180.0) /* id */
+#define CAL2JOULE (4.184) /* id */
+#define E_CHARGE (1.60217733e-19) /* Coulomb */
+
+#define AMU (1.6605402e-27) /* kg */
+#define BOLTZMANN (1.380658e-23) /* (J/K) */
+#define AVOGADRO (6.0221367e23) /* () */
+#define RGAS (BOLTZMANN*AVOGADRO) /* (J/(mol K)) */
+#define BOLTZ (RGAS/KILO) /* (kJ/(mol K)) */
+#define FARADAY (E_CHARGE*AVOGADRO) /* (C/mol) */
+#define ELECTRONVOLT (E_CHARGE*AVOGADRO/KILO) /* (kJ/mol) */
+#define PLANCK1 (6.6262e-34) /* J s */
+#define PLANCK (6.6262e-34*AVOGADRO/(PICO*KILO)) /* (kJ/mol) ps */
+
+#define EPSILON0 (5.72765E-4) /* (e^2 / Na (kJ nm))
+ == (e^2 mol/(kJ nm)) */
+
+#define SPEED_OF_LIGHT (2.9979245800E05) /* nm/ps */
+#define ATOMICMASS_keV (940000.0) /* Atomic mass in keV */
+#define ELECTRONMASS_keV (512.0) /* Electron mas in keV */
+
+/* Improved accuracy (PL & EL, 20090421) */
+#define FACEL (332.0636930*CAL2JOULE)/* (10 * (ONE_4PI_EPS0)) */
+#define ONE_4PI_EPS0 (FACEL*0.1) /* 1/(4*pi*e0)*/
+#define PRESFAC (16.6054) /* bar / pressure unity */
+#define ENM2DEBYE 48.0321 /* Convert electron nm *
+ * to debye */
+#define DEBYE2ENM 0.02081941
+/* to convert from a acceleration in (e V)/(amu nm) */
+/* FIELDFAC is also Faraday's constant and E_CHARGE/(1e6 AMU) */
+#define FIELDFAC (FARADAY/KILO)
+
+/* to convert AU to MD units: */
+#define HARTREE2KJ 4.3597482e-21
+#define BOHR2NM 0.0529177249
+#define HARTREE_BOHR2MD (HARTREE2KJ*AVOGADRO/BOHR2NM)
+
+
+/* The four basic units */
+#define unit_length "nm"
+#define unit_time "ps"
+#define unit_mass "u"
+#define unit_energy "kJ/mol"
+
+/* Temperature unit, T in this unit times BOLTZ give energy in unit_energy */
+#define unit_temp_K "K"
+
+/* Charge unit, electron charge, involves ONE_4PI_EPS0 */
+#define unit_charge_e "e"
+
+/* Pressure unit, pressure in basic units times PRESFAC gives this unit */
+#define unit_pres_bar "bar"
+
+/* Dipole unit, debye, conversion from the unit_charge_e involves ENM2DEBYE */
+#define unit_dipole_D "D"
+
+/* Derived units from basic units only */
+#define unit_vel unit_length "/" unit_time
+#define unit_volume unit_length "^3"
+#define unit_invtime "1/" unit_time
+
+/* Other derived units */
+#define unit_surft_bar unit_pres_bar " " unit_length
+
+/* SI units, conversion from basic units involves NANO, PICO and AMU */
+#define unit_length_SI "m"
+#define unit_time_SI "s"
+#define unit_mass_SI "kg"
+
+#define unit_density_SI unit_mass_SI "/" unit_length_SI "^3"
+#define unit_invvisc_SI unit_length_SI " " unit_time_SI "/" unit_mass_SI
+
+ /* The routines below can be used for converting units from or to GROMACS
+ internal units. */
+ enum { eg2cAngstrom, eg2cNm, eg2cBohr, eg2cKcal_Mole,
+ eg2cHartree, eg2cHartree_e, eg2cAngstrom3, eg2cCoulomb,
+ eg2cDebye, eg2cElectron, eg2cBuckingham, eg2cNR };
+
+ /* Convert value x to GROMACS units. Energy -> Energy, Length -> Length etc.
+ The type of x is deduced from unit,
+ which should be taken from the enum above. */
+ extern double convert2gmx(double x,int unit);
+
+ /* Convert value x from GROMACS units to the desired one.
+ The type of return value is deduced from unit, see above */
+ extern double gmx2convert(double x,int unit);
+
+ /* Convert the string to one of the units supported. Returns -1 if not found. */
+ extern int string2unit(char *string);
+
+ /* Convert the unit to a strong. Return NULL when unit is out of range. */
+ extern const char *unit2string(int unit);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _physics_h */
+
+
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+/*! \file pull_rotation.h
+ *
+ * @brief Enforced rotation of protein parts or other groups of particles.
+ *
+ * This file contains routines that are used to enforce rotational motion
+ * upon a subgroup of particles.
+ *
+ */
+
+#ifndef _pull_rotation_h
+#define _pull_rotation_h
+
+#ifdef HAVE_CONFIG_H
+ #include <config.h>
+#endif
+
+#include "vec.h"
+#include "typedefs.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*! \brief Initialize the enforced rotation groups.
+ *
+ * This routine does the memory allocation for various helper arrays, opens
+ * the output files etc.
+ *
+ * \param fplog General output file, normally md.log.
+ * \param ir Struct containing MD input parameters, among those
+ * also the enforced rotation parameters.
+ * \param nfile Number of entries in the fnm structure.
+ * \param fnm The filenames struct containing also the names
+ * of the rotation output files.
+ * \param cr Pointer to MPI communication data.
+ * \param x The positions of all MD particles.
+ * \param mtop Molecular topology.
+ * \param oenv Needed to open the rotation output xvgr file.
+ * \param Flags Flags passed over from main, used to determine
+ * whether or not we are doing a rerun.
+ */
+extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
+ t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+ gmx_bool bVerbose, unsigned long Flags);
+
+
+/*! \brief Make a selection of the home atoms for all enforced rotation groups.
+ *
+ * This routine is similar to dd_make_local_pull_groups, but works only with
+ * domain decomposition. It should be called at every domain decomposition.
+ *
+ * \param dd Structure containing domain decomposition data.
+ * \param rot Pointer to all the enforced rotation data.
+ */
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot);
+
+
+/*! \brief Calculation of the enforced rotation potential.
+ *
+ * This is the main enforced rotation module which is called during every time
+ * step. Here the rotation potential as well as the resulting forces are
+ * calculated.
+ *
+ * \param cr Pointer to MPI communication data.
+ * \param ir Struct containing MD input parameters, among those
+ * \param box Simulation box, needed to make group whole.
+ * \param x The positions of all the local particles.
+ * \param t Time.
+ * \param step The time step.
+ * \param wcycle During the potential calculation the wallcycles are
+ * counted. Later they enter the dynamic load balancing.
+ * \param bNS After domain decomposition / neighborsearching several
+ * local arrays have to be updated (masses, shifts)
+ */
+extern void do_rotation(t_commrec *cr,t_inputrec *ir,matrix box,rvec x[],real t,
+ gmx_large_int_t step,gmx_wallcycle_t wcycle,gmx_bool bNS);
+
+
+/*! \brief Add the enforced rotation forces to the official force array.
+ *
+ * Adds the forces from enforced rotation potential to the local forces and
+ * sums up the contributions to the rotation potential from all the nodes. Since
+ * this needs communication, this routine should be called after the SR forces
+ * have been evaluated (in order not to spoil cycle counts).
+ * This routine also outputs data to the various rotation output files (e.g.
+ * the potential, the angle of the group, torques and more).
+ *
+ * \param rot Pointer to all the enforced rotation data.
+ * \param f The local forces to which the rotational forces have
+ * to be added.
+ * \param cr Pointer to MPI communication data.
+ * \param step The time step, used for output.
+ * \param t Time, used for output.
+ */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t);
+
+
+/*! \brief Close the enforced rotation output files.
+ *
+ * \param fplog General output file, normally md.log.
+ * \param rot Pointer to all the enforced rotation data.
+ */
+extern void finish_rot(FILE *fplog,t_rot *rot);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+/*! \file
+ * \brief Generic string handling functions.
+ */
+#ifndef _string2_h
+#define _string2_h
+
+/*
+ *
+ * string2.h
+ * David van der Spoel
+ *
+ */
+
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+
+/*#include "typedefs.h"*/
+#include "types/simple.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONTINUE '\\'
+#define COMMENTSIGN ';'
+
+int continuing(char *s);
+
+char *fgets2(char *s, int n, FILE *stream);
+
+void strip_comment (char *line);
+
+int break_line (char *line,
+ char *variable,
+ char *value);
+
+void upstring (char *str);
+
+void ltrim (char *str);
+
+void rtrim (char *str);
+
+void trim (char *str);
+
+void nice_header (FILE *out,const char *fn);
+
+int gmx_strcasecmp_min(const char *str1, const char *str2);
+int gmx_strncasecmp_min(const char *str1, const char *str2, int n);
+/* This funny version of strcasecmp, is not only case-insensitive,
+ * but also ignores '-' and '_'.
+ */
+
+int gmx_strcasecmp(const char *str1, const char *str2);
+int gmx_strncasecmp(const char *str1, const char *str2, int n);
+
+char *gmx_strdup(const char *src);
+char *gmx_strndup(const char *src, int n);
+
+/** Pattern matcing with wildcards. */
+int gmx_wcmatch(const char *pattern, const char *src);
+
+/** Return value for gmx_wcmatch() when there is no match. */
+#define GMX_NO_WCMATCH 1
+
+
+/* this is our implementation of strsep, the thread-safe replacement for
+ strtok */
+char *gmx_strsep(char **stringp, const char *delim);
+
+
+char *wrap_lines(const char *buf,int line_width, int indent,
+ gmx_bool bIndentFirst);
+/* wraps lines at 'linewidth', indenting all following
+ * lines by 'indent' spaces. A temp buffer is allocated and returned,
+ * which can be disposed of if no longer needed.
+ * If !bIndentFirst, then the first line will not be indented, only
+ * the lines that are created due to wapping.
+ */
+
+
+char **split(char sep,const char *str);
+/* Implementation of the well-known Perl function split */
+
+gmx_large_int_t str_to_large_int_t(const char *str, char **endptr);
+
+#if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
+#define snprintf _snprintf
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _string2_h */
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GRoups of Organic Molecules in ACtion for Science
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* note: these enums should correspond to the names in gmxlib/names.c */
+
+enum {
+ epbcXYZ, epbcNONE, epbcXY, epbcSCREW, epbcNR
+};
+
+enum {
+ etcNO, etcBERENDSEN, etcNOSEHOOVER, etcYES, etcANDERSEN, etcANDERSENINTERVAL, etcVRESCALE, etcNR
+}; /* yes is an alias for berendsen */
+
+enum {
+ epcNO, epcBERENDSEN, epcPARRINELLORAHMAN, epcISOTROPIC, epcMTTK, epcNR
+}; /* isotropic is an alias for berendsen */
+
+/* trotter decomposition extended variable parts */
+enum {
+ etrtNONE, etrtNHC, etrtBAROV, etrtBARONHC, etrtNHC2, etrtBAROV2, etrtBARONHC2,
+ etrtVELOCITY1, etrtVELOCITY2, etrtPOSITION, etrtSKIPALL, etrtNR
+};
+
+/* sequenced parts of the trotter decomposition */
+enum {
+ ettTSEQ0, ettTSEQ1, ettTSEQ2, ettTSEQ3, ettTSEQ4, ettTSEQMAX
+};
+
+enum {
+ epctISOTROPIC, epctSEMIISOTROPIC, epctANISOTROPIC,
+ epctSURFACETENSION, epctNR
+};
+
+enum {
+ erscNO, erscALL, erscCOM, erscNR
+};
+
+/*
+ * eelNOTUSED1 used to be GB, but to enable generalized born with different
+ * forms of electrostatics (RF, switch, etc.) in the future it is now selected
+ * separately (through the implicit_solvent option).
+ */
+enum {
+ eelCUT, eelRF, eelGRF, eelPME, eelEWALD, eelPPPM,
+ eelPOISSON, eelSWITCH, eelSHIFT, eelUSER, eelGB_NOTUSED, eelRF_NEC, eelENCADSHIFT,
+ eelPMEUSER, eelPMESWITCH, eelPMEUSERSWITCH, eelRF_ZERO, eelNR
+};
+
+/* Ewald geometry */
+enum {
+ eewg3D, eewg3DC, eewgNR
+};
+
+#define EEL_RF(e) ((e) == eelRF || (e) == eelGRF || (e) == eelRF_NEC || (e) == eelRF_ZERO )
+
+#define EEL_PME(e) ((e) == eelPME || (e) == eelPMESWITCH || (e) == eelPMEUSER || (e) == eelPMEUSERSWITCH)
+#define EEL_FULL(e) (EEL_PME(e) || (e) == eelPPPM || (e) == eelPOISSON || (e) == eelEWALD)
+
+#define EEL_SWITCHED(e) ((e) == eelSWITCH || (e) == eelSHIFT || (e) == eelENCADSHIFT || (e) == eelPMESWITCH || (e) == eelPMEUSERSWITCH)
+
+#define EEL_IS_ZERO_AT_CUTOFF(e) (EEL_SWITCHED(e) || (e) == eelRF_ZERO)
+
+#define EEL_MIGHT_BE_ZERO_AT_CUTOFF(e) (EEL_IS_ZERO_AT_CUTOFF(e) || (e) == eelUSER || (e) == eelPMEUSER)
+
+enum {
+ evdwCUT, evdwSWITCH, evdwSHIFT, evdwUSER, evdwENCADSHIFT, evdwNR
+};
+
+#define EVDW_SWITCHED(e) ((e) == evdwSWITCH || (e) == evdwSHIFT || (e) == evdwENCADSHIFT)
+
+#define EVDW_IS_ZERO_AT_CUTOFF(e) EVDW_SWITCHED(e)
+
+#define EVDW_MIGHT_BE_ZERO_AT_CUTOFF(e) (EVDW_IS_ZERO_AT_CUTOFF(e) || (e) == evdwUSER)
+
+enum {
+ ensGRID, ensSIMPLE, ensNR
+};
+
+/* eiVV is normal velocity verlet -- eiVVAK uses 1/2*(KE(t-dt/2)+KE(t+dt/2)) as the kinetic energy, and the half step kinetic
+ energy for temperature control */
+
+enum {
+ eiMD, eiSteep, eiCG, eiBD, eiSD2, eiNM, eiLBFGS, eiTPI, eiTPIC, eiSD1, eiVV, eiVVAK, eiNR
+};
+#define EI_VV(e) ((e) == eiVV || (e) == eiVVAK)
+#define EI_SD(e) ((e) == eiSD1 || (e) == eiSD2)
+#define EI_RANDOM(e) (EI_SD(e) || (e) == eiBD)
+/*above integrators may not conserve momenta*/
+#define EI_DYNAMICS(e) ((e) == eiMD || EI_SD(e) || (e) == eiBD || EI_VV(e))
+#define EI_ENERGY_MINIMIZATION(e) ((e) == eiSteep || (e) == eiCG || (e) == eiLBFGS)
+#define EI_TPI(e) ((e) == eiTPI || (e) == eiTPIC)
+
+#define EI_STATE_VELOCITY(e) ((e) == eiMD || EI_VV(e) || EI_SD(e))
+
+enum {
+ econtLINCS, econtSHAKE, econtNR
+};
+
+enum {
+ edrNone, edrSimple, edrEnsemble, edrNR
+};
+
+enum {
+ edrwConservative, edrwEqual, edrwNR
+};
+
+/* Combination rule things */
+enum {
+ eCOMB_NONE, eCOMB_GEOMETRIC, eCOMB_ARITHMETIC, eCOMB_GEOM_SIG_EPS, eCOMB_NR
+};
+
+/* NBF selection */
+enum {
+ eNBF_NONE, eNBF_LJ, eNBF_BHAM, eNBF_NR
+};
+
+/* FEP selection */
+enum {
+ efepNO, efepYES, efepNR
+};
+
+/* separate_dhdl_file selection */
+enum
+{
+ /* NOTE: YES is the first one. Do NOT interpret this one as a gmx_bool */
+ sepdhdlfileYES, sepdhdlfileNO, sepdhdlfileNR
+};
+
+/* dhdl_derivatives selection */
+enum
+{
+ /* NOTE: YES is the first one. Do NOT interpret this one as a gmx_bool */
+ dhdlderivativesYES, dhdlderivativesNO, dhdlderivativesNR
+};
+
+/* Solvent model */
+enum {
+ esolNO, esolSPC, esolTIP4P, esolNR
+};
+
+/* Dispersion correction */
+enum {
+ edispcNO, edispcEnerPres, edispcEner, edispcAllEnerPres, edispcAllEner, edispcNR
+};
+
+/* Shell types, for completion stuff */
+enum {
+ eshellCSH, eshellBASH, eshellZSH, eshellNR
+};
+
+/* Center of mass motion selection */
+enum {
+ ecmLINEAR, ecmANGULAR, ecmNO, ecmNR
+};
+
+/* New version of simulated annealing */
+enum {
+ eannNO, eannSINGLE, eannPERIODIC, eannNR
+};
+
+/* Implicit solvent algorithms */
+enum {
+ eisNO, eisGBSA, eisNR
+};
+
+/* Algorithms for calculating GB radii */
+enum {
+ egbSTILL, egbHCT, egbOBC, egbNR
+};
+
+enum {
+ esaAPPROX, esaNO, esaSTILL, esaNR
+};
+
+/* Wall types */
+enum {
+ ewt93, ewt104, ewtTABLE, ewt126, ewtNR
+};
+
+/* Pull stuff */
+enum {
+ epullNO, epullUMBRELLA, epullCONSTRAINT, epullCONST_F, epullNR
+};
+
+enum {
+ epullgDIST, epullgDIR, epullgCYL, epullgPOS, epullgDIRPBC, epullgNR
+};
+
+#define PULL_CYL(pull) ((pull)->eGeom == epullgCYL)
+
+/* Enforced rotation groups */
+enum {
+ erotgISO , erotgISOPF ,
+ erotgPM , erotgPMPF ,
+ erotgRM , erotgRMPF ,
+ erotgRM2 , erotgRM2PF ,
+ erotgFLEX , erotgFLEXT ,
+ erotgFLEX2, erotgFLEX2T,
+ erotgNR
+};
+
+enum {
+ erotgFitRMSD, erotgFitNORM, erotgFitNR
+};
+
+/* QMMM */
+enum {
+ eQMmethodAM1, eQMmethodPM3, eQMmethodRHF,
+ eQMmethodUHF, eQMmethodDFT, eQMmethodB3LYP, eQMmethodMP2, eQMmethodCASSCF, eQMmethodB3LYPLAN,
+ eQMmethodDIRECT, eQMmethodNR
+};
+
+enum {
+ eQMbasisSTO3G, eQMbasisSTO3G2, eQMbasis321G,
+ eQMbasis321Gp, eQMbasis321dGp, eQMbasis621G,
+ eQMbasis631G, eQMbasis631Gp, eQMbasis631dGp,
+ eQMbasis6311G, eQMbasisNR
+};
+
+enum {
+ eQMMMschemenormal,eQMMMschemeoniom,eQMMMschemeNR
+};
+
+enum {
+ eMultentOptName, eMultentOptNo, eMultentOptLast, eMultentOptNR
+};
+
+#ifdef __cplusplus
+}
+#endif
+
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GRoups of Organic Molecules in ACtion for Science
+ */
+#ifndef _inputrec_h_
+#define _inputrec_h_
+
+
+#include "simple.h"
+#include "../sysstuff.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+ int n; /* Number of terms */
+ real *a; /* Coeffients (V / nm ) */
+ real *phi; /* Phase angles */
+} t_cosines;
+
+typedef struct {
+ real E0; /* Field strength (V/nm) */
+ real omega; /* Frequency (1/ps) */
+ real t0; /* Centre of the Gaussian pulse (ps) */
+ real sigma; /* Width of the Gaussian pulse (FWHM) (ps) */
+} t_efield;
+
+#define EGP_EXCL (1<<0)
+#define EGP_TABLE (1<<1)
+
+typedef struct {
+ int ngtc; /* # T-Coupl groups */
+ int nhchainlength; /* # of nose-hoover chains per group */
+ int ngacc; /* # Accelerate groups */
+ int ngfrz; /* # Freeze groups */
+ int ngener; /* # Ener groups */
+ real *nrdf; /* Nr of degrees of freedom in a group */
+ real *ref_t; /* Coupling temperature per group */
+ int *annealing; /* No/simple/periodic SA for each group */
+ int *anneal_npoints; /* Number of annealing time points per grp */
+ real **anneal_time; /* For ea. group: Time points */
+ real **anneal_temp; /* For ea. grp: Temperature at these times */
+ /* Final temp after all intervals is ref_t */
+ real *tau_t; /* Tau coupling time */
+ rvec *acc; /* Acceleration per group */
+ ivec *nFreeze; /* Freeze the group in each direction ? */
+ int *egp_flags; /* Exclusions/tables of energy group pairs */
+
+ /* QMMM stuff */
+ int ngQM; /* nr of QM groups */
+ int *QMmethod; /* Level of theory in the QM calculation */
+ int *QMbasis; /* Basisset in the QM calculation */
+ int *QMcharge; /* Total charge in the QM region */
+ int *QMmult; /* Spin multiplicicty in the QM region */
+ gmx_bool *bSH; /* surface hopping (diabatic hop only) */
+ int *CASorbitals; /* number of orbiatls in the active space */
+ int *CASelectrons;/* number of electrons in the active space */
+ real *SAon; /* at which gap (A.U.) the SA is switched on */
+ real *SAoff;
+ int *SAsteps; /* in how many steps SA goes from 1-1 to 0.5-0.5*/
+ gmx_bool *bOPT;
+ gmx_bool *bTS;
+} t_grpopts;
+
+enum { epgrppbcNONE, epgrppbcREFAT, epgrppbcCOS };
+
+typedef struct {
+ int nat; /* Number of atoms in the pull group */
+ atom_id *ind; /* The global atoms numbers */
+ int nat_loc; /* Number of local pull atoms */
+ int nalloc_loc; /* Allocation size for ind_loc and weight_loc */
+ atom_id *ind_loc; /* Local pull indices */
+ int nweight; /* The number of weights (0 or nat) */
+ real *weight; /* Weights (use all 1 when weight==NULL) */
+ real *weight_loc; /* Weights for the local indices */
+ int epgrppbc; /* The type of pbc for this pull group, see enum above */
+ atom_id pbcatom; /* The reference atom for pbc (global number) */
+ rvec vec; /* The pull vector, direction or position */
+ rvec init; /* Initial reference displacement */
+ real rate; /* Rate of motion (nm/ps) */
+ real k; /* force constant */
+ real kB; /* force constant for state B */
+ real wscale; /* scaling factor for the weights: sum w m/sum w w m */
+ real invtm; /* inverse total mass of the group: 1/wscale sum w m */
+ dvec x; /* center of mass before update */
+ dvec xp; /* center of mass after update before constraining */
+ dvec dr; /* The distance from the reference group */
+ double f_scal; /* Scalar force for directional pulling */
+ dvec f; /* force due to the pulling/constraining */
+} t_pullgrp;
+
+typedef struct {
+ int ngrp; /* number of groups */
+ int eGeom; /* pull geometry */
+ ivec dim; /* used to select components for constraint */
+ real cyl_r1; /* radius of cylinder for dynamic COM */
+ real cyl_r0; /* radius of cylinder including switch length */
+ real constr_tol; /* absolute tolerance for constraints in (nm) */
+ int nstxout; /* Output frequency for pull x */
+ int nstfout; /* Output frequency for pull f */
+ int ePBC; /* the boundary conditions */
+ int npbcdim; /* do pbc in dims 0 <= dim < npbcdim */
+ gmx_bool bRefAt; /* do we need reference atoms for a group COM ? */
+ int cosdim; /* dimension for cosine weighting, -1 if none */
+ gmx_bool bVirial; /* do we need to add the pull virial? */
+ t_pullgrp *grp; /* groups to pull/restrain/etc/ */
+ t_pullgrp *dyna; /* dynamic groups for use with local constraints */
+ rvec *rbuf; /* COM calculation buffer */
+ dvec *dbuf; /* COM calculation buffer */
+ double *dbuf_cyl; /* cylinder ref. groups COM calculation buffer */
+
+ FILE *out_x; /* output file for pull data */
+ FILE *out_f; /* output file for pull data */
+} t_pull;
+
+
+/* Abstract types for enforced rotation only defined in pull_rotation.c */
+typedef struct gmx_enfrot *gmx_enfrot_t;
+typedef struct gmx_enfrotgrp *gmx_enfrotgrp_t;
+
+typedef struct {
+ int eType; /* Rotation type for this group */
+ int bMassW; /* Use mass-weighed positions? */
+ int nat; /* Number of atoms in the group */
+ atom_id *ind; /* The global atoms numbers */
+ rvec *x_ref; /* The reference positions */
+ rvec vec; /* The normalized rotation vector */
+ real rate; /* Rate of rotation (degree/ps) */
+ real k; /* Force constant (kJ/(mol nm^2) */
+ rvec pivot; /* Pivot point of rotation axis (nm) */
+ int eFittype; /* Type of fit to determine actual group angle */
+ real slab_dist; /* Slab distance (nm) */
+ real min_gaussian; /* Minimum value the gaussian must have so that
+ the force is actually evaluated */
+ real eps; /* Additive constant for radial motion2 and
+ flexible2 potentials (nm^2) */
+ gmx_enfrotgrp_t enfrotgrp; /* Stores non-inputrec rotation data per group */
+} t_rotgrp;
+
+typedef struct {
+ int ngrp; /* Number of rotation groups */
+ int nstrout; /* Output frequency for main rotation outfile */
+ int nstsout; /* Output frequency for per-slab data */
+ t_rotgrp *grp; /* Groups to rotate */
+ gmx_enfrot_t enfrot; /* Stores non-inputrec enforced rotation data */
+} t_rot;
+
+
+typedef struct {
+ int eI; /* Integration method */
+ gmx_large_int_t nsteps; /* number of steps to be taken */
+ int simulation_part; /* Used in checkpointing to separate chunks */
+ gmx_large_int_t init_step; /* start at a stepcount >0 (used w. tpbconv) */
+ int nstcalcenergy; /* fequency of energy calc. and T/P coupl. upd. */
+ int ns_type; /* which ns method should we use? */
+ int nstlist; /* number of steps before pairlist is generated */
+ int ndelta; /* number of cells per rlong */
+ int nstcomm; /* number of steps after which center of mass */
+ /* motion is removed */
+ int comm_mode; /* Center of mass motion removal algorithm */
+ int nstcheckpoint; /* checkpointing frequency */
+ int nstlog; /* number of steps after which print to logfile */
+ int nstxout; /* number of steps after which X is output */
+ int nstvout; /* id. for V */
+ int nstfout; /* id. for F */
+ int nstenergy; /* number of steps after which energies printed */
+ int nstxtcout; /* id. for compressed trj (.xtc) */
+ double init_t; /* initial time (ps) */
+ double delta_t; /* time step (ps) */
+ real xtcprec; /* precision of xtc file */
+ int nkx,nky,nkz; /* number of k vectors in each spatial dimension*/
+ /* for fourier methods for long range electrost.*/
+ int pme_order; /* interpolation order for PME */
+ real ewald_rtol; /* Real space tolerance for Ewald, determines */
+ /* the real/reciprocal space relative weight */
+ int ewald_geometry; /* normal/3d ewald, or pseudo-2d LR corrections */
+ real epsilon_surface; /* Epsilon for PME dipole correction */
+ gmx_bool bOptFFT; /* optimize the fft plan at start */
+ int ePBC; /* Type of periodic boundary conditions */
+ int bPeriodicMols; /* Periodic molecules */
+ gmx_bool bContinuation; /* Continuation run: starting state is correct */
+ int etc; /* temperature coupling */
+ int nsttcouple; /* interval in steps for temperature coupling */
+ int epc; /* pressure coupling */
+ int epct; /* pressure coupling type */
+ int nstpcouple; /* interval in steps for pressure coupling */
+ real tau_p; /* pressure coupling time (ps) */
+ tensor ref_p; /* reference pressure (kJ/(mol nm^3)) */
+ tensor compress; /* compressability ((mol nm^3)/kJ) */
+ int refcoord_scaling;/* How to scale absolute reference coordinates */
+ rvec posres_com; /* The COM of the posres atoms */
+ rvec posres_comB; /* The B-state COM of the posres atoms */
+ int andersen_seed; /* Random seed for Andersen thermostat. */
+ real rlist; /* short range pairlist cut-off (nm) */
+ real rlistlong; /* long range pairlist cut-off (nm) */
+ real rtpi; /* Radius for test particle insertion */
+ int coulombtype; /* Type of electrostatics treatment */
+ real rcoulomb_switch; /* Coulomb switch range start (nm) */
+ real rcoulomb; /* Coulomb cutoff (nm) */
+ real epsilon_r; /* relative dielectric constant */
+ real epsilon_rf; /* relative dielectric constant of the RF */
+ int implicit_solvent;/* No (=explicit water), or GBSA solvent models */
+ int gb_algorithm; /* Algorithm to use for calculation Born radii */
+ int nstgbradii; /* Frequency of updating Generalized Born radii */
+ real rgbradii; /* Cutoff for GB radii calculation */
+ real gb_saltconc; /* Salt concentration (M) for GBSA models */
+ real gb_epsilon_solvent; /* dielectric coeff. of implicit solvent */
+ real gb_obc_alpha; /* 1st scaling factor for Bashford-Case GB */
+ real gb_obc_beta; /* 2nd scaling factor for Bashford-Case GB */
+ real gb_obc_gamma; /* 3rd scaling factor for Bashford-Case GB */
+ real gb_dielectric_offset; /* Dielectric offset for Still/HCT/OBC */
+ int sa_algorithm; /* Algorithm for SA part of GBSA */
+ real sa_surface_tension; /* Energy factor for SA part of GBSA */
+ int vdwtype; /* Type of Van der Waals treatment */
+ real rvdw_switch; /* Van der Waals switch range start (nm) */
+ real rvdw; /* Van der Waals cutoff (nm) */
+ int eDispCorr; /* Perform Long range dispersion corrections */
+ real tabext; /* Extension of the table beyond the cut-off, *
+ * as well as the table length for 1-4 interac. */
+ real shake_tol; /* tolerance for shake */
+ int efep; /* free energy interpolation no/yes */
+ double init_lambda; /* initial value for perturbation variable */
+ double delta_lambda; /* change of lambda per time step (1/dt) */
+ int n_flambda; /* The number of foreign lambda points */
+ double *flambda; /* The foreign lambda values */
+ real sc_alpha; /* free energy soft-core parameter */
+ int sc_power; /* lambda power for soft-core interactions */
+ real sc_sigma; /* free energy soft-core sigma when c6 or c12=0 */
+ real sc_sigma_min; /* minimum FE sc sigma (default: =sg_sigma) */
+ int nstdhdl; /* The frequency for writing to dhdl.xvg */
+ int separate_dhdl_file; /* whether to write a separate dhdl.xvg file
+ note: NOT a gmx_bool, but an enum */
+ int dhdl_derivatives;/* whether to calculate+write dhdl derivatives
+ note: NOT a gmx_bool, but an enum */
+ int dh_hist_size; /* The maximum size for the dH histogram */
+ double dh_hist_spacing; /* The spacing for the dH histogram */
+ int eDisre; /* Type of distance restraining */
+ real dr_fc; /* force constant for ta_disre */
+ int eDisreWeighting; /* type of weighting of pairs in one restraints */
+ gmx_bool bDisreMixed; /* Use comb of time averaged and instan. viol's */
+ int nstdisreout; /* frequency of writing pair distances to enx */
+ real dr_tau; /* time constant for memory function in disres */
+ real orires_fc; /* force constant for orientational restraints */
+ real orires_tau; /* time constant for memory function in orires */
+ int nstorireout; /* frequency of writing tr(SD) to enx */
+ real dihre_fc; /* force constant for dihedral restraints */
+ real em_stepsize; /* The stepsize for updating */
+ real em_tol; /* The tolerance */
+ int niter; /* Number of iterations for convergence of */
+ /* steepest descent in relax_shells */
+ real fc_stepsize; /* Stepsize for directional minimization */
+ /* in relax_shells */
+ int nstcgsteep; /* number of steps after which a steepest */
+ /* descents step is done while doing cg */
+ int nbfgscorr; /* Number of corrections to the hessian to keep */
+ int eConstrAlg; /* Type of constraint algorithm */
+ int nProjOrder; /* Order of the LINCS Projection Algorithm */
+ real LincsWarnAngle; /* If bond rotates more than %g degrees, warn */
+ int nLincsIter; /* Number of iterations in the final Lincs step */
+ gmx_bool bShakeSOR; /* Use successive overrelaxation for shake */
+ real bd_fric; /* Friction coefficient for BD (amu/ps) */
+ int ld_seed; /* Random seed for SD and BD */
+ int nwall; /* The number of walls */
+ int wall_type; /* The type of walls */
+ real wall_r_linpot; /* The potentail is linear for r<=wall_r_linpot */
+ int wall_atomtype[2];/* The atom type for walls */
+ real wall_density[2]; /* Number density for walls */
+ real wall_ewald_zfac; /* Scaling factor for the box for Ewald */
+ int ePull; /* Type of pulling: no, umbrella or constraint */
+ t_pull *pull; /* The data for center of mass pulling */
+ gmx_bool bRot; /* Calculate enforced rotation potential(s)? */
+ t_rot *rot; /* The data for enforced rotation potentials */
+ real cos_accel; /* Acceleration for viscosity calculation */
+ tensor deform; /* Triclinic deformation velocities (nm/ps) */
+ int userint1; /* User determined parameters */
+ int userint2;
+ int userint3;
+ int userint4;
+ real userreal1;
+ real userreal2;
+ real userreal3;
+ real userreal4;
+ t_grpopts opts; /* Group options */
+ t_cosines ex[DIM]; /* Electric field stuff (spatial part) */
+ t_cosines et[DIM]; /* Electric field stuff (time part) */
+ gmx_bool bQMMM; /* QM/MM calculation */
+ int QMconstraints; /* constraints on QM bonds */
+ int QMMMscheme; /* Scheme: ONIOM or normal */
+ real scalefactor; /* factor for scaling the MM charges in QM calc.*/
+} t_inputrec;
+
+#define DEFORM(ir) ((ir).deform[XX][XX]!=0 || (ir).deform[YY][YY]!=0 || (ir).deform[ZZ][ZZ]!=0 || (ir).deform[YY][XX]!=0 || (ir).deform[ZZ][XX]!=0 || (ir).deform[ZZ][YY]!=0)
+
+#define DYNAMIC_BOX(ir) ((ir).epc!=epcNO || (ir).eI==eiTPI || DEFORM(ir))
+
+#define PRESERVE_SHAPE(ir) ((ir).epc != epcNO && (ir).deform[XX][XX] == 0 && ((ir).epct == epctISOTROPIC || (ir).epct == epctSEMIISOTROPIC))
+
+#define NEED_MUTOT(ir) (((ir).coulombtype==eelEWALD || EEL_PME((ir).coulombtype)) && ((ir).ewald_geometry==eewg3DC || (ir).epsilon_surface!=0))
+
+#define IR_TWINRANGE(ir) ((ir).rlist > 0 && ((ir).rlistlong == 0 || (ir).rlistlong > (ir).rlist))
+
+#define IR_ELEC_FIELD(ir) ((ir).ex[XX].n > 0 || (ir).ex[YY].n > 0 || (ir).ex[ZZ].n > 0)
+
+#define IR_EXCL_FORCES(ir) (EEL_FULL((ir).coulombtype) || (EEL_RF((ir).coulombtype) && (ir).coulombtype != eelRF_NEC) || (ir).implicit_solvent != eisNO)
+/* use pointer definitions of ir here, since that's what's usually used in the code */
+#define IR_NVT_TROTTER(ir) ((((ir)->eI == eiVV) || ((ir)->eI == eiVVAK)) && ((ir)->etc == etcNOSEHOOVER))
+
+#define IR_NPT_TROTTER(ir) ((((ir)->eI == eiVV) || ((ir)->eI == eiVVAK)) && ((ir)->epc == epcMTTK))
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+#ifndef _vec_h
+#define _vec_h
+
+/*
+ collection of in-line ready operations:
+
+ lookup-table optimized scalar operations:
+ real gmx_invsqrt(real x)
+ void vecinvsqrt(real in[],real out[],int n)
+ void vecrecip(real in[],real out[],int n)
+ real sqr(real x)
+ double dsqr(double x)
+
+ vector operations:
+ void rvec_add(const rvec a,const rvec b,rvec c) c = a + b
+ void dvec_add(const dvec a,const dvec b,dvec c) c = a + b
+ void ivec_add(const ivec a,const ivec b,ivec c) c = a + b
+ void rvec_inc(rvec a,const rvec b) a += b
+ void dvec_inc(dvec a,const dvec b) a += b
+ void ivec_inc(ivec a,const ivec b) a += b
+ void rvec_sub(const rvec a,const rvec b,rvec c) c = a - b
+ void dvec_sub(const dvec a,const dvec b,dvec c) c = a - b
+ void rvec_dec(rvec a,rvec b) a -= b
+ void copy_rvec(const rvec a,rvec b) b = a (reals)
+ void copy_dvec(const dvec a,dvec b) b = a (reals)
+ void copy_ivec(const ivec a,ivec b) b = a (integers)
+ void ivec_sub(const ivec a,const ivec b,ivec c) c = a - b
+ void svmul(real a,rvec v1,rvec v2) v2 = a * v1
+ void dsvmul(double a,dvec v1,dvec v2) v2 = a * v1
+ void clear_rvec(rvec a) a = 0
+ void clear_dvec(dvec a) a = 0
+ void clear_ivec(rvec a) a = 0
+ void clear_rvecs(int n,rvec v[])
+ real iprod(rvec a,rvec b) = a . b (inner product)
+ double diprod(dvec a,dvec b) = a . b (inner product)
+ real iiprod(ivec a,ivec b) = a . b (integers)
+ real norm2(rvec a) = | a |^2 ( = x*y*z )
+ double dnorm2(dvec a) = | a |^2 ( = x*y*z )
+ real norm(rvec a) = | a |
+ double dnorm(dvec a) = | a |
+ void cprod(rvec a,rvec b,rvec c) c = a x b (cross product)
+ void dprod(rvec a,rvec b,rvec c) c = a x b (cross product)
+ void dprod(rvec a,rvec b,rvec c) c = a * b (direct product)
+ real cos_angle(rvec a,rvec b)
+ real cos_angle_no_table(rvec a,rvec b)
+ real distance2(rvec v1, rvec v2) = | v2 - v1 |^2
+ void unitv(rvec src,rvec dest) dest = src / |src|
+ void unitv_no_table(rvec src,rvec dest) dest = src / |src|
+
+ matrix (3x3) operations:
+ ! indicates that dest should not be the same as a, b or src
+ the _ur0 varieties work on matrices that have only zeros
+ in the upper right part, such as box matrices, these varieties
+ could produce less rounding errors, not due to the operations themselves,
+ but because the compiler can easier recombine the operations
+ void copy_mat(matrix a,matrix b) b = a
+ void clear_mat(matrix a) a = 0
+ void mmul(matrix a,matrix b,matrix dest) ! dest = a . b
+ void mmul_ur0(matrix a,matrix b,matrix dest) dest = a . b
+ void transpose(matrix src,matrix dest) ! dest = src*
+ void tmmul(matrix a,matrix b,matrix dest) ! dest = a* . b
+ void mtmul(matrix a,matrix b,matrix dest) ! dest = a . b*
+ real det(matrix a) = det(a)
+ void m_add(matrix a,matrix b,matrix dest) dest = a + b
+ void m_sub(matrix a,matrix b,matrix dest) dest = a - b
+ void msmul(matrix m1,real r1,matrix dest) dest = r1 * m1
+ void m_inv_ur0(matrix src,matrix dest) dest = src^-1
+ void m_inv(matrix src,matrix dest) ! dest = src^-1
+ void mvmul(matrix a,rvec src,rvec dest) ! dest = a . src
+ void mvmul_ur0(matrix a,rvec src,rvec dest) dest = a . src
+ void tmvmul_ur0(matrix a,rvec src,rvec dest) dest = a* . src
+ real trace(matrix m) = trace(m)
+*/
+
+#include "types/simple.h"
+#include "maths.h"
+#include "typedefs.h"
+#include "sysstuff.h"
+#include "macros.h"
+#include "gmx_fatal.h"
+#include "mpelogging.h"
+#include "physics.h"
+
+#ifdef __cplusplus
+extern "C" {
+#elif 0
+} /* avoid screwing up indentation */
+#endif
+
+
+#define EXP_LSB 0x00800000
+#define EXP_MASK 0x7f800000
+#define EXP_SHIFT 23
+#define FRACT_MASK 0x007fffff
+#define FRACT_SIZE 11 /* significant part of fraction */
+#define FRACT_SHIFT (EXP_SHIFT-FRACT_SIZE)
+#define EXP_ADDR(val) (((val)&EXP_MASK)>>EXP_SHIFT)
+#define FRACT_ADDR(val) (((val)&(FRACT_MASK|EXP_LSB))>>FRACT_SHIFT)
+
+#define PR_VEC(a) a[XX],a[YY],a[ZZ]
+
+#ifdef GMX_SOFTWARE_INVSQRT
+extern const unsigned int * gmx_invsqrt_exptab;
+extern const unsigned int * gmx_invsqrt_fracttab;
+#endif
+
+
+typedef union
+{
+ unsigned int bval;
+ float fval;
+} t_convert;
+
+
+#ifdef GMX_SOFTWARE_INVSQRT
+static real gmx_invsqrt(real x)
+{
+ const real half=0.5;
+ const real three=3.0;
+ t_convert result,bit_pattern;
+ unsigned int exp,fract;
+ real lu;
+ real y;
+#ifdef GMX_DOUBLE
+ real y2;
+#endif
+
+ bit_pattern.fval=x;
+ exp = EXP_ADDR(bit_pattern.bval);
+ fract = FRACT_ADDR(bit_pattern.bval);
+ result.bval=gmx_invsqrt_exptab[exp] | gmx_invsqrt_fracttab[fract];
+ lu = result.fval;
+
+ y=(half*lu*(three-((x*lu)*lu)));
+#ifdef GMX_DOUBLE
+ y2=(half*y*(three-((x*y)*y)));
+
+ return y2; /* 10 Flops */
+#else
+ return y; /* 5 Flops */
+#endif
+}
+#define INVSQRT_DONE
+#endif /* gmx_invsqrt */
+
+#ifdef GMX_POWERPC_SQRT
+static real gmx_invsqrt(real x)
+{
+ const real half=0.5;
+ const real three=3.0;
+ t_convert result,bit_pattern;
+ unsigned int exp,fract;
+ real lu;
+ real y;
+#ifdef GMX_DOUBLE
+ real y2;
+#endif
+
+ lu = __frsqrte((double)x);
+
+ y=(half*lu*(three-((x*lu)*lu)));
+
+#if (GMX_POWERPC_SQRT==2)
+ /* Extra iteration required */
+ y=(half*y*(three-((x*y)*y)));
+#endif
+
+#ifdef GMX_DOUBLE
+ y2=(half*y*(three-((x*y)*y)));
+
+ return y2; /* 10 Flops */
+#else
+ return y; /* 5 Flops */
+#endif
+}
+#define INVSQRT_DONE
+#endif /* powerpc_invsqrt */
+
+
+#ifndef INVSQRT_DONE
+#define gmx_invsqrt(x) (1.0f/sqrt(x))
+#endif
+
+
+
+
+
+static real sqr(real x)
+{
+ return (x*x);
+}
+
+static gmx_inline double dsqr(double x)
+{
+ return (x*x);
+}
+
+/* Maclaurin series for sinh(x)/x, useful for NH chains and MTTK pressure control
+ Here, we compute it to 10th order, which might be overkill, 8th is probably enough,
+ but it's not very much more expensive. */
+
+static gmx_inline real series_sinhx(real x)
+{
+ real x2 = x*x;
+ return (1 + (x2/6.0)*(1 + (x2/20.0)*(1 + (x2/42.0)*(1 + (x2/72.0)*(1 + (x2/110.0))))));
+}
+
+void vecinvsqrt(real in[],real out[],int n);
+/* Perform out[i]=1.0/sqrt(in[i]) for n elements */
+
+
+void vecrecip(real in[],real out[],int n);
+/* Perform out[i]=1.0/(in[i]) for n elements */
+
+/* Note: If you need a fast version of vecinvsqrt
+ * and/or vecrecip, call detectcpu() and run the SSE/3DNow/SSE2/Altivec
+ * versions if your hardware supports it.
+ *
+ * To use those routines, your memory HAS TO BE CACHE-ALIGNED.
+ * Use snew_aligned(ptr,size,32) to allocate and sfree_aligned to free.
+ */
+
+
+static gmx_inline void rvec_add(const rvec a,const rvec b,rvec c)
+{
+ real x,y,z;
+
+ x=a[XX]+b[XX];
+ y=a[YY]+b[YY];
+ z=a[ZZ]+b[ZZ];
+
+ c[XX]=x;
+ c[YY]=y;
+ c[ZZ]=z;
+}
+
+static gmx_inline void dvec_add(const dvec a,const dvec b,dvec c)
+{
+ double x,y,z;
+
+ x=a[XX]+b[XX];
+ y=a[YY]+b[YY];
+ z=a[ZZ]+b[ZZ];
+
+ c[XX]=x;
+ c[YY]=y;
+ c[ZZ]=z;
+}
+
+static gmx_inline void ivec_add(const ivec a,const ivec b,ivec c)
+{
+ int x,y,z;
+
+ x=a[XX]+b[XX];
+ y=a[YY]+b[YY];
+ z=a[ZZ]+b[ZZ];
+
+ c[XX]=x;
+ c[YY]=y;
+ c[ZZ]=z;
+}
+
+static gmx_inline void rvec_inc(rvec a,const rvec b)
+{
+ real x,y,z;
+
+ x=a[XX]+b[XX];
+ y=a[YY]+b[YY];
+ z=a[ZZ]+b[ZZ];
+
+ a[XX]=x;
+ a[YY]=y;
+ a[ZZ]=z;
+}
+
+static gmx_inline void dvec_inc(dvec a,const dvec b)
+{
+ double x,y,z;
+
+ x=a[XX]+b[XX];
+ y=a[YY]+b[YY];
+ z=a[ZZ]+b[ZZ];
+
+ a[XX]=x;
+ a[YY]=y;
+ a[ZZ]=z;
+}
+
+static gmx_inline void rvec_sub(const rvec a,const rvec b,rvec c)
+{
+ real x,y,z;
+
+ x=a[XX]-b[XX];
+ y=a[YY]-b[YY];
+ z=a[ZZ]-b[ZZ];
+
+ c[XX]=x;
+ c[YY]=y;
+ c[ZZ]=z;
+}
+
+static gmx_inline void dvec_sub(const dvec a,const dvec b,dvec c)
+{
+ double x,y,z;
+
+ x=a[XX]-b[XX];
+ y=a[YY]-b[YY];
+ z=a[ZZ]-b[ZZ];
+
+ c[XX]=x;
+ c[YY]=y;
+ c[ZZ]=z;
+}
+
+static gmx_inline void rvec_dec(rvec a,const rvec b)
+{
+ real x,y,z;
+
+ x=a[XX]-b[XX];
+ y=a[YY]-b[YY];
+ z=a[ZZ]-b[ZZ];
+
+ a[XX]=x;
+ a[YY]=y;
+ a[ZZ]=z;
+}
+
+static gmx_inline void copy_rvec(const rvec a,rvec b)
+{
+ b[XX]=a[XX];
+ b[YY]=a[YY];
+ b[ZZ]=a[ZZ];
+}
+
+static gmx_inline void copy_rvecn(rvec *a,rvec *b,int startn, int endn)
+{
+ int i;
+ for (i=startn;i<endn;i++) {
+ b[i][XX]=a[i][XX];
+ b[i][YY]=a[i][YY];
+ b[i][ZZ]=a[i][ZZ];
+ }
+}
+
+static gmx_inline void copy_dvec(const dvec a,dvec b)
+{
+ b[XX]=a[XX];
+ b[YY]=a[YY];
+ b[ZZ]=a[ZZ];
+}
+
+static gmx_inline void copy_ivec(const ivec a,ivec b)
+{
+ b[XX]=a[XX];
+ b[YY]=a[YY];
+ b[ZZ]=a[ZZ];
+}
+
+static gmx_inline void ivec_sub(const ivec a,const ivec b,ivec c)
+{
+ int x,y,z;
+
+ x=a[XX]-b[XX];
+ y=a[YY]-b[YY];
+ z=a[ZZ]-b[ZZ];
+
+ c[XX]=x;
+ c[YY]=y;
+ c[ZZ]=z;
+}
+
+static gmx_inline void copy_mat(matrix a,matrix b)
+{
+ copy_rvec(a[XX],b[XX]);
+ copy_rvec(a[YY],b[YY]);
+ copy_rvec(a[ZZ],b[ZZ]);
+}
+
+static gmx_inline void svmul(real a,const rvec v1,rvec v2)
+{
+ v2[XX]=a*v1[XX];
+ v2[YY]=a*v1[YY];
+ v2[ZZ]=a*v1[ZZ];
+}
+
+static gmx_inline void dsvmul(double a,const dvec v1,dvec v2)
+{
+ v2[XX]=a*v1[XX];
+ v2[YY]=a*v1[YY];
+ v2[ZZ]=a*v1[ZZ];
+}
+
+static gmx_inline real distance2(const rvec v1,const rvec v2)
+{
+ return sqr(v2[XX]-v1[XX]) + sqr(v2[YY]-v1[YY]) + sqr(v2[ZZ]-v1[ZZ]);
+}
+
+static gmx_inline void clear_rvec(rvec a)
+{
+ /* The ibm compiler has problems with inlining this
+ * when we use a const real variable
+ */
+ a[XX]=0.0;
+ a[YY]=0.0;
+ a[ZZ]=0.0;
+}
+
+static gmx_inline void clear_dvec(dvec a)
+{
+ /* The ibm compiler has problems with inlining this
+ * when we use a const real variable
+ */
+ a[XX]=0.0;
+ a[YY]=0.0;
+ a[ZZ]=0.0;
+}
+
+static gmx_inline void clear_ivec(ivec a)
+{
+ a[XX]=0;
+ a[YY]=0;
+ a[ZZ]=0;
+}
+
+static gmx_inline void clear_rvecs(int n,rvec v[])
+{
+/* memset(v[0],0,DIM*n*sizeof(v[0][0])); */
+ int i;
+
+ GMX_MPE_LOG(ev_clear_rvecs_start);
+
+ for(i=0; (i<n); i++)
+ clear_rvec(v[i]);
+
+ GMX_MPE_LOG(ev_clear_rvecs_finish);
+}
+
+static gmx_inline void clear_mat(matrix a)
+{
+/* memset(a[0],0,DIM*DIM*sizeof(a[0][0])); */
+
+ const real nul=0.0;
+
+ a[XX][XX]=a[XX][YY]=a[XX][ZZ]=nul;
+ a[YY][XX]=a[YY][YY]=a[YY][ZZ]=nul;
+ a[ZZ][XX]=a[ZZ][YY]=a[ZZ][ZZ]=nul;
+}
+
+static gmx_inline real iprod(const rvec a,const rvec b)
+{
+ return (a[XX]*b[XX]+a[YY]*b[YY]+a[ZZ]*b[ZZ]);
+}
+
+static gmx_inline double diprod(const dvec a,const dvec b)
+{
+ return (a[XX]*b[XX]+a[YY]*b[YY]+a[ZZ]*b[ZZ]);
+}
+
+static gmx_inline int iiprod(const ivec a,const ivec b)
+{
+ return (a[XX]*b[XX]+a[YY]*b[YY]+a[ZZ]*b[ZZ]);
+}
+
+static gmx_inline real norm2(const rvec a)
+{
+ return a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ];
+}
+
+static gmx_inline double dnorm2(const dvec a)
+{
+ return a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ];
+}
+
+static gmx_inline real norm(const rvec a)
+{
+ return (real)sqrt(a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ]);
+}
+
+static gmx_inline double dnorm(const dvec a)
+{
+ return sqrt(a[XX]*a[XX]+a[YY]*a[YY]+a[ZZ]*a[ZZ]);
+}
+
+/* WARNING:
+ * Do _not_ use these routines to calculate the angle between two vectors
+ * as acos(cos_angle(u,v)). While it might seem obvious, the acos function
+ * is very flat close to -1 and 1, which will lead to accuracy-loss.
+ * Instead, use the new gmx_angle() function directly.
+ */
+static gmx_inline real
+cos_angle(const rvec a,const rvec b)
+{
+ /*
+ * ax*bx + ay*by + az*bz
+ * cos-vec (a,b) = ---------------------
+ * ||a|| * ||b||
+ */
+ real cosval;
+ int m;
+ double aa,bb,ip,ipa,ipb,ipab; /* For accuracy these must be double! */
+
+ ip=ipa=ipb=0.0;
+ for(m=0; (m<DIM); m++) { /* 18 */
+ aa = a[m];
+ bb = b[m];
+ ip += aa*bb;
+ ipa += aa*aa;
+ ipb += bb*bb;
+ }
+ ipab = ipa*ipb;
+ if (ipab > 0)
+ cosval = ip*gmx_invsqrt(ipab); /* 7 */
+ else
+ cosval = 1;
+ /* 25 TOTAL */
+ if (cosval > 1.0)
+ return 1.0;
+ if (cosval <-1.0)
+ return -1.0;
+
+ return cosval;
+}
+
+/* WARNING:
+ * Do _not_ use these routines to calculate the angle between two vectors
+ * as acos(cos_angle(u,v)). While it might seem obvious, the acos function
+ * is very flat close to -1 and 1, which will lead to accuracy-loss.
+ * Instead, use the new gmx_angle() function directly.
+ */
+static gmx_inline real
+cos_angle_no_table(const rvec a,const rvec b)
+{
+ /* This version does not need the invsqrt lookup table */
+ real cosval;
+ int m;
+ double aa,bb,ip,ipa,ipb; /* For accuracy these must be double! */
+
+ ip=ipa=ipb=0.0;
+ for(m=0; (m<DIM); m++) { /* 18 */
+ aa = a[m];
+ bb = b[m];
+ ip += aa*bb;
+ ipa += aa*aa;
+ ipb += bb*bb;
+ }
+ cosval=ip/sqrt(ipa*ipb); /* 12 */
+ /* 30 TOTAL */
+ if (cosval > 1.0)
+ return 1.0;
+ if (cosval <-1.0)
+ return -1.0;
+
+ return cosval;
+}
+
+
+static gmx_inline void cprod(const rvec a,const rvec b,rvec c)
+{
+ c[XX]=a[YY]*b[ZZ]-a[ZZ]*b[YY];
+ c[YY]=a[ZZ]*b[XX]-a[XX]*b[ZZ];
+ c[ZZ]=a[XX]*b[YY]-a[YY]*b[XX];
+}
+
+static gmx_inline void dcprod(const dvec a,const dvec b,dvec c)
+{
+ c[XX]=a[YY]*b[ZZ]-a[ZZ]*b[YY];
+ c[YY]=a[ZZ]*b[XX]-a[XX]*b[ZZ];
+ c[ZZ]=a[XX]*b[YY]-a[YY]*b[XX];
+}
+
+/* This routine calculates the angle between a & b without any loss of accuracy close to 0/PI.
+ * If you only need cos(theta), use the cos_angle() routines to save a few cycles.
+ * This routine is faster than it might appear, since atan2 is accelerated on many CPUs (e.g. x86).
+ */
+static gmx_inline real
+gmx_angle(const rvec a, const rvec b)
+{
+ rvec w;
+ real wlen,s;
+
+ cprod(a,b,w);
+
+ wlen = norm(w);
+ s = iprod(a,b);
+
+ return atan2(wlen,s);
+}
+
+static gmx_inline void mmul_ur0(matrix a,matrix b,matrix dest)
+{
+ dest[XX][XX]=a[XX][XX]*b[XX][XX];
+ dest[XX][YY]=0.0;
+ dest[XX][ZZ]=0.0;
+ dest[YY][XX]=a[YY][XX]*b[XX][XX]+a[YY][YY]*b[YY][XX];
+ dest[YY][YY]= a[YY][YY]*b[YY][YY];
+ dest[YY][ZZ]=0.0;
+ dest[ZZ][XX]=a[ZZ][XX]*b[XX][XX]+a[ZZ][YY]*b[YY][XX]+a[ZZ][ZZ]*b[ZZ][XX];
+ dest[ZZ][YY]= a[ZZ][YY]*b[YY][YY]+a[ZZ][ZZ]*b[ZZ][YY];
+ dest[ZZ][ZZ]= a[ZZ][ZZ]*b[ZZ][ZZ];
+}
+
+static gmx_inline void mmul(matrix a,matrix b,matrix dest)
+{
+ dest[XX][XX]=a[XX][XX]*b[XX][XX]+a[XX][YY]*b[YY][XX]+a[XX][ZZ]*b[ZZ][XX];
+ dest[YY][XX]=a[YY][XX]*b[XX][XX]+a[YY][YY]*b[YY][XX]+a[YY][ZZ]*b[ZZ][XX];
+ dest[ZZ][XX]=a[ZZ][XX]*b[XX][XX]+a[ZZ][YY]*b[YY][XX]+a[ZZ][ZZ]*b[ZZ][XX];
+ dest[XX][YY]=a[XX][XX]*b[XX][YY]+a[XX][YY]*b[YY][YY]+a[XX][ZZ]*b[ZZ][YY];
+ dest[YY][YY]=a[YY][XX]*b[XX][YY]+a[YY][YY]*b[YY][YY]+a[YY][ZZ]*b[ZZ][YY];
+ dest[ZZ][YY]=a[ZZ][XX]*b[XX][YY]+a[ZZ][YY]*b[YY][YY]+a[ZZ][ZZ]*b[ZZ][YY];
+ dest[XX][ZZ]=a[XX][XX]*b[XX][ZZ]+a[XX][YY]*b[YY][ZZ]+a[XX][ZZ]*b[ZZ][ZZ];
+ dest[YY][ZZ]=a[YY][XX]*b[XX][ZZ]+a[YY][YY]*b[YY][ZZ]+a[YY][ZZ]*b[ZZ][ZZ];
+ dest[ZZ][ZZ]=a[ZZ][XX]*b[XX][ZZ]+a[ZZ][YY]*b[YY][ZZ]+a[ZZ][ZZ]*b[ZZ][ZZ];
+}
+
+static gmx_inline void transpose(matrix src,matrix dest)
+{
+ dest[XX][XX]=src[XX][XX];
+ dest[YY][XX]=src[XX][YY];
+ dest[ZZ][XX]=src[XX][ZZ];
+ dest[XX][YY]=src[YY][XX];
+ dest[YY][YY]=src[YY][YY];
+ dest[ZZ][YY]=src[YY][ZZ];
+ dest[XX][ZZ]=src[ZZ][XX];
+ dest[YY][ZZ]=src[ZZ][YY];
+ dest[ZZ][ZZ]=src[ZZ][ZZ];
+}
+
+static gmx_inline void tmmul(matrix a,matrix b,matrix dest)
+{
+ /* Computes dest=mmul(transpose(a),b,dest) - used in do_pr_pcoupl */
+ dest[XX][XX]=a[XX][XX]*b[XX][XX]+a[YY][XX]*b[YY][XX]+a[ZZ][XX]*b[ZZ][XX];
+ dest[XX][YY]=a[XX][XX]*b[XX][YY]+a[YY][XX]*b[YY][YY]+a[ZZ][XX]*b[ZZ][YY];
+ dest[XX][ZZ]=a[XX][XX]*b[XX][ZZ]+a[YY][XX]*b[YY][ZZ]+a[ZZ][XX]*b[ZZ][ZZ];
+ dest[YY][XX]=a[XX][YY]*b[XX][XX]+a[YY][YY]*b[YY][XX]+a[ZZ][YY]*b[ZZ][XX];
+ dest[YY][YY]=a[XX][YY]*b[XX][YY]+a[YY][YY]*b[YY][YY]+a[ZZ][YY]*b[ZZ][YY];
+ dest[YY][ZZ]=a[XX][YY]*b[XX][ZZ]+a[YY][YY]*b[YY][ZZ]+a[ZZ][YY]*b[ZZ][ZZ];
+ dest[ZZ][XX]=a[XX][ZZ]*b[XX][XX]+a[YY][ZZ]*b[YY][XX]+a[ZZ][ZZ]*b[ZZ][XX];
+ dest[ZZ][YY]=a[XX][ZZ]*b[XX][YY]+a[YY][ZZ]*b[YY][YY]+a[ZZ][ZZ]*b[ZZ][YY];
+ dest[ZZ][ZZ]=a[XX][ZZ]*b[XX][ZZ]+a[YY][ZZ]*b[YY][ZZ]+a[ZZ][ZZ]*b[ZZ][ZZ];
+}
+
+static gmx_inline void mtmul(matrix a,matrix b,matrix dest)
+{
+ /* Computes dest=mmul(a,transpose(b),dest) - used in do_pr_pcoupl */
+ dest[XX][XX]=a[XX][XX]*b[XX][XX]+a[XX][YY]*b[XX][YY]+a[XX][ZZ]*b[XX][ZZ];
+ dest[XX][YY]=a[XX][XX]*b[YY][XX]+a[XX][YY]*b[YY][YY]+a[XX][ZZ]*b[YY][ZZ];
+ dest[XX][ZZ]=a[XX][XX]*b[ZZ][XX]+a[XX][YY]*b[ZZ][YY]+a[XX][ZZ]*b[ZZ][ZZ];
+ dest[YY][XX]=a[YY][XX]*b[XX][XX]+a[YY][YY]*b[XX][YY]+a[YY][ZZ]*b[XX][ZZ];
+ dest[YY][YY]=a[YY][XX]*b[YY][XX]+a[YY][YY]*b[YY][YY]+a[YY][ZZ]*b[YY][ZZ];
+ dest[YY][ZZ]=a[YY][XX]*b[ZZ][XX]+a[YY][YY]*b[ZZ][YY]+a[YY][ZZ]*b[ZZ][ZZ];
+ dest[ZZ][XX]=a[ZZ][XX]*b[XX][XX]+a[ZZ][YY]*b[XX][YY]+a[ZZ][ZZ]*b[XX][ZZ];
+ dest[ZZ][YY]=a[ZZ][XX]*b[YY][XX]+a[ZZ][YY]*b[YY][YY]+a[ZZ][ZZ]*b[YY][ZZ];
+ dest[ZZ][ZZ]=a[ZZ][XX]*b[ZZ][XX]+a[ZZ][YY]*b[ZZ][YY]+a[ZZ][ZZ]*b[ZZ][ZZ];
+}
+
+static gmx_inline real det(matrix a)
+{
+ return ( a[XX][XX]*(a[YY][YY]*a[ZZ][ZZ]-a[ZZ][YY]*a[YY][ZZ])
+ -a[YY][XX]*(a[XX][YY]*a[ZZ][ZZ]-a[ZZ][YY]*a[XX][ZZ])
+ +a[ZZ][XX]*(a[XX][YY]*a[YY][ZZ]-a[YY][YY]*a[XX][ZZ]));
+}
+
+static gmx_inline void m_add(matrix a,matrix b,matrix dest)
+{
+ dest[XX][XX]=a[XX][XX]+b[XX][XX];
+ dest[XX][YY]=a[XX][YY]+b[XX][YY];
+ dest[XX][ZZ]=a[XX][ZZ]+b[XX][ZZ];
+ dest[YY][XX]=a[YY][XX]+b[YY][XX];
+ dest[YY][YY]=a[YY][YY]+b[YY][YY];
+ dest[YY][ZZ]=a[YY][ZZ]+b[YY][ZZ];
+ dest[ZZ][XX]=a[ZZ][XX]+b[ZZ][XX];
+ dest[ZZ][YY]=a[ZZ][YY]+b[ZZ][YY];
+ dest[ZZ][ZZ]=a[ZZ][ZZ]+b[ZZ][ZZ];
+}
+
+static gmx_inline void m_sub(matrix a,matrix b,matrix dest)
+{
+ dest[XX][XX]=a[XX][XX]-b[XX][XX];
+ dest[XX][YY]=a[XX][YY]-b[XX][YY];
+ dest[XX][ZZ]=a[XX][ZZ]-b[XX][ZZ];
+ dest[YY][XX]=a[YY][XX]-b[YY][XX];
+ dest[YY][YY]=a[YY][YY]-b[YY][YY];
+ dest[YY][ZZ]=a[YY][ZZ]-b[YY][ZZ];
+ dest[ZZ][XX]=a[ZZ][XX]-b[ZZ][XX];
+ dest[ZZ][YY]=a[ZZ][YY]-b[ZZ][YY];
+ dest[ZZ][ZZ]=a[ZZ][ZZ]-b[ZZ][ZZ];
+}
+
+static gmx_inline void msmul(matrix m1,real r1,matrix dest)
+{
+ dest[XX][XX]=r1*m1[XX][XX];
+ dest[XX][YY]=r1*m1[XX][YY];
+ dest[XX][ZZ]=r1*m1[XX][ZZ];
+ dest[YY][XX]=r1*m1[YY][XX];
+ dest[YY][YY]=r1*m1[YY][YY];
+ dest[YY][ZZ]=r1*m1[YY][ZZ];
+ dest[ZZ][XX]=r1*m1[ZZ][XX];
+ dest[ZZ][YY]=r1*m1[ZZ][YY];
+ dest[ZZ][ZZ]=r1*m1[ZZ][ZZ];
+}
+
+static gmx_inline void m_inv_ur0(matrix src,matrix dest)
+{
+ double tmp = src[XX][XX]*src[YY][YY]*src[ZZ][ZZ];
+ if (fabs(tmp) <= 100*GMX_REAL_MIN)
+ gmx_fatal(FARGS,"Can not invert matrix, determinant is zero");
+
+ dest[XX][XX] = 1/src[XX][XX];
+ dest[YY][YY] = 1/src[YY][YY];
+ dest[ZZ][ZZ] = 1/src[ZZ][ZZ];
+ dest[ZZ][XX] = (src[YY][XX]*src[ZZ][YY]*dest[YY][YY]
+ - src[ZZ][XX])*dest[XX][XX]*dest[ZZ][ZZ];
+ dest[YY][XX] = -src[YY][XX]*dest[XX][XX]*dest[YY][YY];
+ dest[ZZ][YY] = -src[ZZ][YY]*dest[YY][YY]*dest[ZZ][ZZ];
+ dest[XX][YY] = 0.0;
+ dest[XX][ZZ] = 0.0;
+ dest[YY][ZZ] = 0.0;
+}
+
+static gmx_inline void m_inv(matrix src,matrix dest)
+{
+ const real smallreal = (real)1.0e-24;
+ const real largereal = (real)1.0e24;
+ real deter,c,fc;
+
+ deter = det(src);
+ c = (real)1.0/deter;
+ fc = (real)fabs(c);
+
+ if ((fc <= smallreal) || (fc >= largereal))
+ gmx_fatal(FARGS,"Can not invert matrix, determinant = %e",deter);
+
+ dest[XX][XX]= c*(src[YY][YY]*src[ZZ][ZZ]-src[ZZ][YY]*src[YY][ZZ]);
+ dest[XX][YY]=-c*(src[XX][YY]*src[ZZ][ZZ]-src[ZZ][YY]*src[XX][ZZ]);
+ dest[XX][ZZ]= c*(src[XX][YY]*src[YY][ZZ]-src[YY][YY]*src[XX][ZZ]);
+ dest[YY][XX]=-c*(src[YY][XX]*src[ZZ][ZZ]-src[ZZ][XX]*src[YY][ZZ]);
+ dest[YY][YY]= c*(src[XX][XX]*src[ZZ][ZZ]-src[ZZ][XX]*src[XX][ZZ]);
+ dest[YY][ZZ]=-c*(src[XX][XX]*src[YY][ZZ]-src[YY][XX]*src[XX][ZZ]);
+ dest[ZZ][XX]= c*(src[YY][XX]*src[ZZ][YY]-src[ZZ][XX]*src[YY][YY]);
+ dest[ZZ][YY]=-c*(src[XX][XX]*src[ZZ][YY]-src[ZZ][XX]*src[XX][YY]);
+ dest[ZZ][ZZ]= c*(src[XX][XX]*src[YY][YY]-src[YY][XX]*src[XX][YY]);
+}
+
+static gmx_inline void mvmul(matrix a,const rvec src,rvec dest)
+{
+ dest[XX]=a[XX][XX]*src[XX]+a[XX][YY]*src[YY]+a[XX][ZZ]*src[ZZ];
+ dest[YY]=a[YY][XX]*src[XX]+a[YY][YY]*src[YY]+a[YY][ZZ]*src[ZZ];
+ dest[ZZ]=a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
+}
+
+static gmx_inline void mvmul_ur0(matrix a,const rvec src,rvec dest)
+{
+ dest[ZZ]=a[ZZ][XX]*src[XX]+a[ZZ][YY]*src[YY]+a[ZZ][ZZ]*src[ZZ];
+ dest[YY]=a[YY][XX]*src[XX]+a[YY][YY]*src[YY];
+ dest[XX]=a[XX][XX]*src[XX];
+}
+
+static gmx_inline void tmvmul_ur0(matrix a,const rvec src,rvec dest)
+{
+ dest[XX]=a[XX][XX]*src[XX]+a[YY][XX]*src[YY]+a[ZZ][XX]*src[ZZ];
+ dest[YY]= a[YY][YY]*src[YY]+a[ZZ][YY]*src[ZZ];
+ dest[ZZ]= a[ZZ][ZZ]*src[ZZ];
+}
+
+static gmx_inline void unitv(const rvec src,rvec dest)
+{
+ real linv;
+
+ linv=gmx_invsqrt(norm2(src));
+ dest[XX]=linv*src[XX];
+ dest[YY]=linv*src[YY];
+ dest[ZZ]=linv*src[ZZ];
+}
+
+static gmx_inline void unitv_no_table(const rvec src,rvec dest)
+{
+ real linv;
+
+ linv=1.0/sqrt(norm2(src));
+ dest[XX]=linv*src[XX];
+ dest[YY]=linv*src[YY];
+ dest[ZZ]=linv*src[ZZ];
+}
+
+static void calc_lll(rvec box,rvec lll)
+{
+ lll[XX] = 2.0*M_PI/box[XX];
+ lll[YY] = 2.0*M_PI/box[YY];
+ lll[ZZ] = 2.0*M_PI/box[ZZ];
+}
+
+static gmx_inline real trace(matrix m)
+{
+ return (m[XX][XX]+m[YY][YY]+m[ZZ][ZZ]);
+}
+
+static gmx_inline real _divide_err(real a,real b,const char *file,int line)
+{
+ if (fabs(b) <= GMX_REAL_MIN)
+ gmx_fatal(FARGS,"Dividing by zero, file %s, line %d",file,line);
+ return a/b;
+}
+
+static gmx_inline int _mod(int a,int b,char *file,int line)
+{
+ if(b==0)
+ gmx_fatal(FARGS,"Modulo zero, file %s, line %d",file,line);
+ return a % b;
+}
+
+/* Operations on multidimensional rvecs, used e.g. in edsam.c */
+static void m_rveccopy(int dim, rvec *a, rvec *b)
+{
+ /* b = a */
+ int i;
+
+ for (i=0; i<dim; i++)
+ copy_rvec(a[i],b[i]);
+}
+
+/*computer matrix vectors from base vectors and angles */
+static void matrix_convert(matrix box, rvec vec, rvec angle)
+{
+ svmul(DEG2RAD,angle,angle);
+ box[XX][XX] = vec[XX];
+ box[YY][XX] = vec[YY]*cos(angle[ZZ]);
+ box[YY][YY] = vec[YY]*sin(angle[ZZ]);
+ box[ZZ][XX] = vec[ZZ]*cos(angle[YY]);
+ box[ZZ][YY] = vec[ZZ]
+ *(cos(angle[XX])-cos(angle[YY])*cos(angle[ZZ]))/sin(angle[ZZ]);
+ box[ZZ][ZZ] = sqrt(sqr(vec[ZZ])
+ -box[ZZ][XX]*box[ZZ][XX]-box[ZZ][YY]*box[ZZ][YY]);
+}
+
+#define divide_err(a,b) _divide_err((a),(b),__FILE__,__LINE__)
+#define mod(a,b) _mod((a),(b),__FILE__,__LINE__)
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _vec_h */
--- /dev/null
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCL_INSTALL_DIR@
+
+Name: libgromacs
+Description: Gromacs library
+URL: http://www.gromacs.org
+Version: @PROJECT_VERSION@
+Requires: @PKG_FFT@ @PKG_XML@
+Libs.private: -lm @CMAKE_THREAD_LIBS_INIT@
+Libs: -L${libdir} -lmd@LIBSUFFIX@ @PKG_FFT_LIBS@
+Cflags: -I${includedir} @PKG_CFLAGS@
+
--- /dev/null
+file(GLOB MDLIB_SOURCES *.c)
+
+# Files called xxx_test.c are test drivers with a main() function for
+# module xxx.c, so they should not be included in the library
+file(GLOB_RECURSE NOT_MDLIB_SOURCES *_test.c)
+list(REMOVE_ITEM MDLIB_SOURCES ${NOT_MDLIB_SOURCES})
+set(MDLIB_SOURCES ${MDLIB_SOURCES} PARENT_SCOPE)
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This file is part of Gromacs Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ *
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "typedefs.h"
+#include "smalloc.h"
+#include "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 "names.h"
+#include "pdbio.h"
+#include "futil.h"
+#include "force.h"
+#include "pme.h"
+#include "pull.h"
+#include "pull_rotation.h"
+#include "gmx_wallcycle.h"
+#include "mdrun.h"
+#include "nsgrid.h"
+#include "shellfc.h"
+#include "mtop_util.h"
+#include "gmxfio.h"
+#include "gmx_ga2la.h"
+#include "gmx_sort.h"
+
+#ifdef GMX_LIB_MPI
+#include <mpi.h>
+#endif
+#ifdef GMX_THREADS
+#include "tmpi.h"
+#endif
+
+#define DDRANK(dd,rank) (rank)
+#define DDMASTERRANK(dd) (dd->masterrank)
+
+typedef struct gmx_domdec_master
+{
+ /* The cell boundaries */
+ real **cell_x;
+ /* The global charge group division */
+ int *ncg; /* Number of home charge groups for each node */
+ int *index; /* Index of nnodes+1 into cg */
+ int *cg; /* Global charge group index */
+ int *nat; /* Number of home atoms for each node. */
+ int *ibuf; /* Buffer for communication */
+ rvec *vbuf; /* Buffer for state scattering and gathering */
+} gmx_domdec_master_t;
+
+typedef struct
+{
+ /* The numbers of charge groups to send and receive for each cell
+ * that requires communication, the last entry contains the total
+ * number of atoms that needs to be communicated.
+ */
+ int nsend[DD_MAXIZONE+2];
+ int nrecv[DD_MAXIZONE+2];
+ /* The charge groups to send */
+ int *index;
+ int nalloc;
+ /* The atom range for non-in-place communication */
+ int cell2at0[DD_MAXIZONE];
+ int cell2at1[DD_MAXIZONE];
+} gmx_domdec_ind_t;
+
+typedef struct
+{
+ int np; /* Number of grid pulses in this dimension */
+ int np_dlb; /* For dlb, for use with edlbAUTO */
+ gmx_domdec_ind_t *ind; /* The indices to communicate, size np */
+ int np_nalloc;
+ gmx_bool bInPlace; /* Can we communicate in place? */
+} gmx_domdec_comm_dim_t;
+
+typedef struct
+{
+ gmx_bool *bCellMin; /* Temp. var.: is this cell size at the limit */
+ real *cell_f; /* State var.: cell boundaries, box relative */
+ real *old_cell_f; /* Temp. var.: old cell size */
+ real *cell_f_max0; /* State var.: max lower boundary, incl neighbors */
+ real *cell_f_min1; /* State var.: min upper boundary, incl neighbors */
+ real *bound_min; /* Temp. var.: lower limit for cell boundary */
+ real *bound_max; /* Temp. var.: upper limit for cell boundary */
+ gmx_bool bLimited; /* State var.: is DLB limited in this dim and row */
+ real *buf_ncd; /* Temp. var. */
+} gmx_domdec_root_t;
+
+#define DD_NLOAD_MAX 9
+
+/* Here floats are accurate enough, since these variables
+ * only influence the load balancing, not the actual MD results.
+ */
+typedef struct
+{
+ int nload;
+ float *load;
+ float sum;
+ float max;
+ float sum_m;
+ float cvol_min;
+ float mdf;
+ float pme;
+ int flags;
+} gmx_domdec_load_t;
+
+typedef struct
+{
+ int nsc;
+ int ind_gl;
+ int ind;
+} gmx_cgsort_t;
+
+typedef struct
+{
+ gmx_cgsort_t *sort1,*sort2;
+ int sort_nalloc;
+ gmx_cgsort_t *sort_new;
+ int sort_new_nalloc;
+ int *ibuf;
+ int ibuf_nalloc;
+} gmx_domdec_sort_t;
+
+typedef struct
+{
+ rvec *v;
+ int nalloc;
+} vec_rvec_t;
+
+/* This enum determines the order of the coordinates.
+ * ddnatHOME and ddnatZONE should be first and second,
+ * the others can be ordered as wanted.
+ */
+enum { ddnatHOME, ddnatZONE, ddnatVSITE, ddnatCON, ddnatNR };
+
+enum { edlbAUTO, edlbNO, edlbYES, edlbNR };
+const char *edlb_names[edlbNR] = { "auto", "no", "yes" };
+
+typedef struct
+{
+ int dim; /* The dimension */
+ gmx_bool dim_match;/* Tells if DD and PME dims match */
+ int nslab; /* The number of PME slabs in this dimension */
+ real *slb_dim_f; /* Cell sizes for determining the PME comm. with SLB */
+ int *pp_min; /* The minimum pp node location, size nslab */
+ int *pp_max; /* The maximum pp node location,size nslab */
+ int maxshift; /* The maximum shift for coordinate redistribution in PME */
+} gmx_ddpme_t;
+
+typedef struct
+{
+ real min0; /* The minimum bottom of this zone */
+ real max1; /* The maximum top of this zone */
+ real mch0; /* The maximum bottom communicaton height for this zone */
+ real mch1; /* The maximum top communicaton height for this zone */
+ real p1_0; /* The bottom value of the first cell in this zone */
+ real p1_1; /* The top value of the first cell in this zone */
+} gmx_ddzone_t;
+
+typedef struct gmx_domdec_comm
+{
+ /* All arrays are indexed with 0 to dd->ndim (not Cartesian indexing),
+ * unless stated otherwise.
+ */
+
+ /* The number of decomposition dimensions for PME, 0: no PME */
+ int npmedecompdim;
+ /* The number of nodes doing PME (PP/PME or only PME) */
+ int npmenodes;
+ int npmenodes_x;
+ int npmenodes_y;
+ /* The communication setup including the PME only nodes */
+ gmx_bool bCartesianPP_PME;
+ ivec ntot;
+ int cartpmedim;
+ int *pmenodes; /* size npmenodes */
+ int *ddindex2simnodeid; /* size npmenodes, only with bCartesianPP
+ * but with bCartesianPP_PME */
+ gmx_ddpme_t ddpme[2];
+
+ /* The DD particle-particle nodes only */
+ gmx_bool bCartesianPP;
+ int *ddindex2ddnodeid; /* size npmenode, only with bCartesianPP_PME */
+
+ /* The global charge groups */
+ t_block cgs_gl;
+
+ /* Should we sort the cgs */
+ int nstSortCG;
+ gmx_domdec_sort_t *sort;
+
+ /* Are there bonded and multi-body interactions between charge groups? */
+ gmx_bool bInterCGBondeds;
+ gmx_bool bInterCGMultiBody;
+
+ /* Data for the optional bonded interaction atom communication range */
+ gmx_bool bBondComm;
+ t_blocka *cglink;
+ char *bLocalCG;
+
+ /* The DLB option */
+ int eDLB;
+ /* Are we actually using DLB? */
+ gmx_bool bDynLoadBal;
+
+ /* Cell sizes for static load balancing, first index cartesian */
+ real **slb_frac;
+
+ /* The width of the communicated boundaries */
+ real cutoff_mbody;
+ real cutoff;
+ /* The minimum cell size (including triclinic correction) */
+ rvec cellsize_min;
+ /* For dlb, for use with edlbAUTO */
+ rvec cellsize_min_dlb;
+ /* The lower limit for the DD cell size with DLB */
+ real cellsize_limit;
+ /* Effectively no NB cut-off limit with DLB for systems without PBC? */
+ gmx_bool bVacDLBNoLimit;
+
+ /* tric_dir is only stored here because dd_get_ns_ranges needs it */
+ ivec tric_dir;
+ /* box0 and box_size are required with dim's without pbc and -gcom */
+ rvec box0;
+ rvec box_size;
+
+ /* The cell boundaries */
+ rvec cell_x0;
+ rvec cell_x1;
+
+ /* The old location of the cell boundaries, to check cg displacements */
+ rvec old_cell_x0;
+ rvec old_cell_x1;
+
+ /* The communication setup and charge group boundaries for the zones */
+ gmx_domdec_zones_t zones;
+
+ /* The zone limits for DD dimensions 1 and 2 (not 0), determined from
+ * cell boundaries of neighboring cells for dynamic load balancing.
+ */
+ gmx_ddzone_t zone_d1[2];
+ gmx_ddzone_t zone_d2[2][2];
+
+ /* The coordinate/force communication setup and indices */
+ gmx_domdec_comm_dim_t cd[DIM];
+ /* The maximum number of cells to communicate with in one dimension */
+ int maxpulse;
+
+ /* Which cg distribution is stored on the master node */
+ int master_cg_ddp_count;
+
+ /* The number of cg's received from the direct neighbors */
+ int zone_ncg1[DD_MAXZONE];
+
+ /* The atom counts, the range for each type t is nat[t-1] <= at < nat[t] */
+ int nat[ddnatNR];
+
+ /* Communication buffer for general use */
+ int *buf_int;
+ int nalloc_int;
+
+ /* Communication buffer for general use */
+ vec_rvec_t vbuf;
+
+ /* Communication buffers only used with multiple grid pulses */
+ int *buf_int2;
+ int nalloc_int2;
+ vec_rvec_t vbuf2;
+
+ /* Communication buffers for local redistribution */
+ int **cggl_flag;
+ int cggl_flag_nalloc[DIM*2];
+ rvec **cgcm_state;
+ int cgcm_state_nalloc[DIM*2];
+
+ /* Cell sizes for dynamic load balancing */
+ gmx_domdec_root_t **root;
+ real *cell_f_row;
+ real cell_f0[DIM];
+ real cell_f1[DIM];
+ real cell_f_max0[DIM];
+ real cell_f_min1[DIM];
+
+ /* Stuff for load communication */
+ gmx_bool bRecordLoad;
+ gmx_domdec_load_t *load;
+#ifdef GMX_MPI
+ MPI_Comm *mpi_comm_load;
+#endif
+ /* Cycle counters */
+ float cycl[ddCyclNr];
+ int cycl_n[ddCyclNr];
+ float cycl_max[ddCyclNr];
+ /* Flop counter (0=no,1=yes,2=with (eFlop-1)*5% noise */
+ int eFlop;
+ double flop;
+ int flop_n;
+ /* Have often have did we have load measurements */
+ int n_load_have;
+ /* Have often have we collected the load measurements */
+ int n_load_collect;
+
+ /* Statistics */
+ double sum_nat[ddnatNR-ddnatZONE];
+ int ndecomp;
+ int nload;
+ double load_step;
+ double load_sum;
+ double load_max;
+ ivec load_lim;
+ double load_mdf;
+ double load_pme;
+
+ /* The last partition step */
+ gmx_large_int_t partition_step;
+
+ /* Debugging */
+ int nstDDDump;
+ int nstDDDumpGrid;
+ int DD_debug;
+} gmx_domdec_comm_t;
+
+/* The size per charge group of the cggl_flag buffer in gmx_domdec_comm_t */
+#define DD_CGIBS 2
+
+/* The flags for the cggl_flag buffer in gmx_domdec_comm_t */
+#define DD_FLAG_NRCG 65535
+#define DD_FLAG_FW(d) (1<<(16+(d)*2))
+#define DD_FLAG_BW(d) (1<<(16+(d)*2+1))
+
+/* Zone permutation required to obtain consecutive charge groups
+ * for neighbor searching.
+ */
+static const int zone_perm[3][4] = { {0,0,0,0},{1,0,0,0},{3,0,1,2} };
+
+/* dd_zo and dd_zp3/dd_zp2 are set up such that i zones with non-zero
+ * components see only j zones with that component 0.
+ */
+
+/* The DD zone order */
+static const ivec dd_zo[DD_MAXZONE] =
+ {{0,0,0},{1,0,0},{1,1,0},{0,1,0},{0,1,1},{0,0,1},{1,0,1},{1,1,1}};
+
+/* The 3D setup */
+#define dd_z3n 8
+#define dd_zp3n 4
+static const ivec dd_zp3[dd_zp3n] = {{0,0,8},{1,3,6},{2,5,6},{3,5,7}};
+
+/* The 2D setup */
+#define dd_z2n 4
+#define dd_zp2n 2
+static const ivec dd_zp2[dd_zp2n] = {{0,0,4},{1,3,4}};
+
+/* The 1D setup */
+#define dd_z1n 2
+#define dd_zp1n 1
+static const ivec dd_zp1[dd_zp1n] = {{0,0,2}};
+
+/* Factors used to avoid problems due to rounding issues */
+#define DD_CELL_MARGIN 1.0001
+#define DD_CELL_MARGIN2 1.00005
+/* Factor to account for pressure scaling during nstlist steps */
+#define DD_PRES_SCALE_MARGIN 1.02
+
+/* Allowed performance loss before we DLB or warn */
+#define DD_PERF_LOSS 0.05
+
+#define DD_CELL_F_SIZE(dd,di) ((dd)->nc[(dd)->dim[(di)]]+1+(di)*2+1+(di))
+
+/* Use separate MPI send and receive commands
+ * when nnodes <= GMX_DD_NNODES_SENDRECV.
+ * This saves memory (and some copying for small nnodes).
+ * For high parallelization scatter and gather calls are used.
+ */
+#define GMX_DD_NNODES_SENDRECV 4
+
+
+/*
+#define dd_index(n,i) ((((i)[ZZ]*(n)[YY] + (i)[YY])*(n)[XX]) + (i)[XX])
+
+static void index2xyz(ivec nc,int ind,ivec xyz)
+{
+ xyz[XX] = ind % nc[XX];
+ xyz[YY] = (ind / nc[XX]) % nc[YY];
+ xyz[ZZ] = ind / (nc[YY]*nc[XX]);
+}
+*/
+
+/* This order is required to minimize the coordinate communication in PME
+ * which uses decomposition in the x direction.
+ */
+#define dd_index(n,i) ((((i)[XX]*(n)[YY] + (i)[YY])*(n)[ZZ]) + (i)[ZZ])
+
+static void ddindex2xyz(ivec nc,int ind,ivec xyz)
+{
+ xyz[XX] = ind / (nc[YY]*nc[ZZ]);
+ xyz[YY] = (ind / nc[ZZ]) % nc[YY];
+ xyz[ZZ] = ind % nc[ZZ];
+}
+
+static int ddcoord2ddnodeid(gmx_domdec_t *dd,ivec c)
+{
+ int ddindex;
+ int ddnodeid=-1;
+
+ ddindex = dd_index(dd->nc,c);
+ if (dd->comm->bCartesianPP_PME)
+ {
+ ddnodeid = dd->comm->ddindex2ddnodeid[ddindex];
+ }
+ else if (dd->comm->bCartesianPP)
+ {
+#ifdef GMX_MPI
+ MPI_Cart_rank(dd->mpi_comm_all,c,&ddnodeid);
+#endif
+ }
+ else
+ {
+ ddnodeid = ddindex;
+ }
+
+ return ddnodeid;
+}
+
+static gmx_bool dynamic_dd_box(gmx_ddbox_t *ddbox,t_inputrec *ir)
+{
+ return (ddbox->nboundeddim < DIM || DYNAMIC_BOX(*ir));
+}
+
+int ddglatnr(gmx_domdec_t *dd,int i)
+{
+ int atnr;
+
+ if (dd == NULL)
+ {
+ atnr = i + 1;
+ }
+ else
+ {
+ if (i >= dd->comm->nat[ddnatNR-1])
+ {
+ gmx_fatal(FARGS,"glatnr called with %d, which is larger than the local number of atoms (%d)",i,dd->comm->nat[ddnatNR-1]);
+ }
+ atnr = dd->gatindex[i] + 1;
+ }
+
+ return atnr;
+}
+
+t_block *dd_charge_groups_global(gmx_domdec_t *dd)
+{
+ return &dd->comm->cgs_gl;
+}
+
+static void vec_rvec_init(vec_rvec_t *v)
+{
+ v->nalloc = 0;
+ v->v = NULL;
+}
+
+static void vec_rvec_check_alloc(vec_rvec_t *v,int n)
+{
+ if (n > v->nalloc)
+ {
+ v->nalloc = over_alloc_dd(n);
+ srenew(v->v,v->nalloc);
+ }
+}
+
+void dd_store_state(gmx_domdec_t *dd,t_state *state)
+{
+ int i;
+
+ if (state->ddp_count != dd->ddp_count)
+ {
+ gmx_incons("The state does not the domain decomposition state");
+ }
+
+ state->ncg_gl = dd->ncg_home;
+ if (state->ncg_gl > state->cg_gl_nalloc)
+ {
+ state->cg_gl_nalloc = over_alloc_dd(state->ncg_gl);
+ srenew(state->cg_gl,state->cg_gl_nalloc);
+ }
+ for(i=0; i<state->ncg_gl; i++)
+ {
+ state->cg_gl[i] = dd->index_gl[i];
+ }
+
+ state->ddp_count_cg_gl = dd->ddp_count;
+}
+
+gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd)
+{
+ return &dd->comm->zones;
+}
+
+void dd_get_ns_ranges(gmx_domdec_t *dd,int icg,
+ int *jcg0,int *jcg1,ivec shift0,ivec shift1)
+{
+ gmx_domdec_zones_t *zones;
+ int izone,d,dim;
+
+ zones = &dd->comm->zones;
+
+ izone = 0;
+ while (icg >= zones->izone[izone].cg1)
+ {
+ izone++;
+ }
+
+ if (izone == 0)
+ {
+ *jcg0 = icg;
+ }
+ else if (izone < zones->nizone)
+ {
+ *jcg0 = zones->izone[izone].jcg0;
+ }
+ else
+ {
+ gmx_fatal(FARGS,"DD icg %d out of range: izone (%d) >= nizone (%d)",
+ icg,izone,zones->nizone);
+ }
+
+ *jcg1 = zones->izone[izone].jcg1;
+
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ shift0[dim] = zones->izone[izone].shift0[dim];
+ shift1[dim] = zones->izone[izone].shift1[dim];
+ if (dd->comm->tric_dir[dim] || (dd->bGridJump && d > 0))
+ {
+ /* A conservative approach, this can be optimized */
+ shift0[dim] -= 1;
+ shift1[dim] += 1;
+ }
+ }
+}
+
+int dd_natoms_vsite(gmx_domdec_t *dd)
+{
+ return dd->comm->nat[ddnatVSITE];
+}
+
+void dd_get_constraint_range(gmx_domdec_t *dd,int *at_start,int *at_end)
+{
+ *at_start = dd->comm->nat[ddnatCON-1];
+ *at_end = dd->comm->nat[ddnatCON];
+}
+
+void dd_move_x(gmx_domdec_t *dd,matrix box,rvec x[])
+{
+ int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
+ int *index,*cgindex;
+ gmx_domdec_comm_t *comm;
+ gmx_domdec_comm_dim_t *cd;
+ gmx_domdec_ind_t *ind;
+ rvec shift={0,0,0},*buf,*rbuf;
+ gmx_bool bPBC,bScrew;
+
+ comm = dd->comm;
+
+ cgindex = dd->cgindex;
+
+ buf = comm->vbuf.v;
+
+ nzone = 1;
+ nat_tot = dd->nat_home;
+ for(d=0; d<dd->ndim; d++)
+ {
+ bPBC = (dd->ci[dd->dim[d]] == 0);
+ bScrew = (bPBC && dd->bScrewPBC && dd->dim[d] == XX);
+ if (bPBC)
+ {
+ copy_rvec(box[dd->dim[d]],shift);
+ }
+ cd = &comm->cd[d];
+ for(p=0; p<cd->np; p++)
+ {
+ ind = &cd->ind[p];
+ index = ind->index;
+ n = 0;
+ if (!bPBC)
+ {
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ copy_rvec(x[j],buf[n]);
+ n++;
+ }
+ }
+ }
+ else if (!bScrew)
+ {
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ /* We need to shift the coordinates */
+ rvec_add(x[j],shift,buf[n]);
+ n++;
+ }
+ }
+ }
+ else
+ {
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ /* Shift x */
+ buf[n][XX] = x[j][XX] + shift[XX];
+ /* Rotate y and z.
+ * This operation requires a special shift force
+ * treatment, which is performed in calc_vir.
+ */
+ buf[n][YY] = box[YY][YY] - x[j][YY];
+ buf[n][ZZ] = box[ZZ][ZZ] - x[j][ZZ];
+ n++;
+ }
+ }
+ }
+
+ if (cd->bInPlace)
+ {
+ rbuf = x + nat_tot;
+ }
+ else
+ {
+ rbuf = comm->vbuf2.v;
+ }
+ /* Send and receive the coordinates */
+ dd_sendrecv_rvec(dd, d, dddirBackward,
+ buf, ind->nsend[nzone+1],
+ rbuf, ind->nrecv[nzone+1]);
+ if (!cd->bInPlace)
+ {
+ j = 0;
+ for(zone=0; zone<nzone; zone++)
+ {
+ for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
+ {
+ copy_rvec(rbuf[j],x[i]);
+ j++;
+ }
+ }
+ }
+ nat_tot += ind->nrecv[nzone+1];
+ }
+ nzone += nzone;
+ }
+}
+
+void dd_move_f(gmx_domdec_t *dd,rvec f[],rvec *fshift)
+{
+ int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
+ int *index,*cgindex;
+ gmx_domdec_comm_t *comm;
+ gmx_domdec_comm_dim_t *cd;
+ gmx_domdec_ind_t *ind;
+ rvec *buf,*sbuf;
+ ivec vis;
+ int is;
+ gmx_bool bPBC,bScrew;
+
+ comm = dd->comm;
+
+ cgindex = dd->cgindex;
+
+ buf = comm->vbuf.v;
+
+ n = 0;
+ nzone = comm->zones.n/2;
+ nat_tot = dd->nat_tot;
+ for(d=dd->ndim-1; d>=0; d--)
+ {
+ bPBC = (dd->ci[dd->dim[d]] == 0);
+ bScrew = (bPBC && dd->bScrewPBC && dd->dim[d] == XX);
+ if (fshift == NULL && !bScrew)
+ {
+ bPBC = FALSE;
+ }
+ /* Determine which shift vector we need */
+ clear_ivec(vis);
+ vis[dd->dim[d]] = 1;
+ is = IVEC2IS(vis);
+
+ cd = &comm->cd[d];
+ for(p=cd->np-1; p>=0; p--) {
+ ind = &cd->ind[p];
+ nat_tot -= ind->nrecv[nzone+1];
+ if (cd->bInPlace)
+ {
+ sbuf = f + nat_tot;
+ }
+ else
+ {
+ sbuf = comm->vbuf2.v;
+ j = 0;
+ for(zone=0; zone<nzone; zone++)
+ {
+ for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
+ {
+ copy_rvec(f[i],sbuf[j]);
+ j++;
+ }
+ }
+ }
+ /* Communicate the forces */
+ dd_sendrecv_rvec(dd, d, dddirForward,
+ sbuf, ind->nrecv[nzone+1],
+ buf, ind->nsend[nzone+1]);
+ index = ind->index;
+ /* Add the received forces */
+ n = 0;
+ if (!bPBC)
+ {
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ rvec_inc(f[j],buf[n]);
+ n++;
+ }
+ }
+ }
+ else if (!bScrew)
+ {
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ rvec_inc(f[j],buf[n]);
+ /* Add this force to the shift force */
+ rvec_inc(fshift[is],buf[n]);
+ n++;
+ }
+ }
+ }
+ else
+ {
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ /* Rotate the force */
+ f[j][XX] += buf[n][XX];
+ f[j][YY] -= buf[n][YY];
+ f[j][ZZ] -= buf[n][ZZ];
+ if (fshift)
+ {
+ /* Add this force to the shift force */
+ rvec_inc(fshift[is],buf[n]);
+ }
+ n++;
+ }
+ }
+ }
+ }
+ nzone /= 2;
+ }
+}
+
+void dd_atom_spread_real(gmx_domdec_t *dd,real v[])
+{
+ int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
+ int *index,*cgindex;
+ gmx_domdec_comm_t *comm;
+ gmx_domdec_comm_dim_t *cd;
+ gmx_domdec_ind_t *ind;
+ real *buf,*rbuf;
+
+ comm = dd->comm;
+
+ cgindex = dd->cgindex;
+
+ buf = &comm->vbuf.v[0][0];
+
+ nzone = 1;
+ nat_tot = dd->nat_home;
+ for(d=0; d<dd->ndim; d++)
+ {
+ cd = &comm->cd[d];
+ for(p=0; p<cd->np; p++)
+ {
+ ind = &cd->ind[p];
+ index = ind->index;
+ n = 0;
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ buf[n] = v[j];
+ n++;
+ }
+ }
+
+ if (cd->bInPlace)
+ {
+ rbuf = v + nat_tot;
+ }
+ else
+ {
+ rbuf = &comm->vbuf2.v[0][0];
+ }
+ /* Send and receive the coordinates */
+ dd_sendrecv_real(dd, d, dddirBackward,
+ buf, ind->nsend[nzone+1],
+ rbuf, ind->nrecv[nzone+1]);
+ if (!cd->bInPlace)
+ {
+ j = 0;
+ for(zone=0; zone<nzone; zone++)
+ {
+ for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
+ {
+ v[i] = rbuf[j];
+ j++;
+ }
+ }
+ }
+ nat_tot += ind->nrecv[nzone+1];
+ }
+ nzone += nzone;
+ }
+}
+
+void dd_atom_sum_real(gmx_domdec_t *dd,real v[])
+{
+ int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
+ int *index,*cgindex;
+ gmx_domdec_comm_t *comm;
+ gmx_domdec_comm_dim_t *cd;
+ gmx_domdec_ind_t *ind;
+ real *buf,*sbuf;
+
+ comm = dd->comm;
+
+ cgindex = dd->cgindex;
+
+ buf = &comm->vbuf.v[0][0];
+
+ n = 0;
+ nzone = comm->zones.n/2;
+ nat_tot = dd->nat_tot;
+ for(d=dd->ndim-1; d>=0; d--)
+ {
+ cd = &comm->cd[d];
+ for(p=cd->np-1; p>=0; p--) {
+ ind = &cd->ind[p];
+ nat_tot -= ind->nrecv[nzone+1];
+ if (cd->bInPlace)
+ {
+ sbuf = v + nat_tot;
+ }
+ else
+ {
+ sbuf = &comm->vbuf2.v[0][0];
+ j = 0;
+ for(zone=0; zone<nzone; zone++)
+ {
+ for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
+ {
+ sbuf[j] = v[i];
+ j++;
+ }
+ }
+ }
+ /* Communicate the forces */
+ dd_sendrecv_real(dd, d, dddirForward,
+ sbuf, ind->nrecv[nzone+1],
+ buf, ind->nsend[nzone+1]);
+ index = ind->index;
+ /* Add the received forces */
+ n = 0;
+ for(i=0; i<ind->nsend[nzone]; i++)
+ {
+ at0 = cgindex[index[i]];
+ at1 = cgindex[index[i]+1];
+ for(j=at0; j<at1; j++)
+ {
+ v[j] += buf[n];
+ n++;
+ }
+ }
+ }
+ nzone /= 2;
+ }
+}
+
+static void print_ddzone(FILE *fp,int d,int i,int j,gmx_ddzone_t *zone)
+{
+ fprintf(fp,"zone d0 %d d1 %d d2 %d min0 %6.3f max1 %6.3f mch0 %6.3f mch1 %6.3f p1_0 %6.3f p1_1 %6.3f\n",
+ d,i,j,
+ zone->min0,zone->max1,
+ zone->mch0,zone->mch0,
+ zone->p1_0,zone->p1_1);
+}
+
+static void dd_sendrecv_ddzone(const gmx_domdec_t *dd,
+ int ddimind,int direction,
+ gmx_ddzone_t *buf_s,int n_s,
+ gmx_ddzone_t *buf_r,int n_r)
+{
+ rvec vbuf_s[5*2],vbuf_r[5*2];
+ int i;
+
+ for(i=0; i<n_s; i++)
+ {
+ vbuf_s[i*2 ][0] = buf_s[i].min0;
+ vbuf_s[i*2 ][1] = buf_s[i].max1;
+ vbuf_s[i*2 ][2] = buf_s[i].mch0;
+ vbuf_s[i*2+1][0] = buf_s[i].mch1;
+ vbuf_s[i*2+1][1] = buf_s[i].p1_0;
+ vbuf_s[i*2+1][2] = buf_s[i].p1_1;
+ }
+
+ dd_sendrecv_rvec(dd, ddimind, direction,
+ vbuf_s, n_s*2,
+ vbuf_r, n_r*2);
+
+ for(i=0; i<n_r; i++)
+ {
+ buf_r[i].min0 = vbuf_r[i*2 ][0];
+ buf_r[i].max1 = vbuf_r[i*2 ][1];
+ buf_r[i].mch0 = vbuf_r[i*2 ][2];
+ buf_r[i].mch1 = vbuf_r[i*2+1][0];
+ buf_r[i].p1_0 = vbuf_r[i*2+1][1];
+ buf_r[i].p1_1 = vbuf_r[i*2+1][2];
+ }
+}
+
+static void dd_move_cellx(gmx_domdec_t *dd,gmx_ddbox_t *ddbox,
+ rvec cell_ns_x0,rvec cell_ns_x1)
+{
+ int d,d1,dim,dim1,pos,buf_size,i,j,k,p,npulse,npulse_min;
+ gmx_ddzone_t *zp,buf_s[5],buf_r[5],buf_e[5];
+ rvec extr_s[2],extr_r[2];
+ rvec dh;
+ real dist_d,c=0,det;
+ gmx_domdec_comm_t *comm;
+ gmx_bool bPBC,bUse;
+
+ comm = dd->comm;
+
+ for(d=1; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ zp = (d == 1) ? &comm->zone_d1[0] : &comm->zone_d2[0][0];
+ zp->min0 = cell_ns_x0[dim];
+ zp->max1 = cell_ns_x1[dim];
+ zp->mch0 = cell_ns_x0[dim];
+ zp->mch1 = cell_ns_x1[dim];
+ zp->p1_0 = cell_ns_x0[dim];
+ zp->p1_1 = cell_ns_x1[dim];
+ }
+
+ for(d=dd->ndim-2; d>=0; d--)
+ {
+ dim = dd->dim[d];
+ bPBC = (dim < ddbox->npbcdim);
+
+ /* Use an rvec to store two reals */
+ extr_s[d][0] = comm->cell_f0[d+1];
+ extr_s[d][1] = comm->cell_f1[d+1];
+ extr_s[d][2] = 0;
+
+ pos = 0;
+ /* Store the extremes in the backward sending buffer,
+ * so the get updated separately from the forward communication.
+ */
+ for(d1=d; d1<dd->ndim-1; d1++)
+ {
+ /* We invert the order to be able to use the same loop for buf_e */
+ buf_s[pos].min0 = extr_s[d1][1];
+ buf_s[pos].max1 = extr_s[d1][0];
+ buf_s[pos].mch0 = 0;
+ buf_s[pos].mch1 = 0;
+ /* Store the cell corner of the dimension we communicate along */
+ buf_s[pos].p1_0 = comm->cell_x0[dim];
+ buf_s[pos].p1_1 = 0;
+ pos++;
+ }
+
+ buf_s[pos] = (dd->ndim == 2) ? comm->zone_d1[0] : comm->zone_d2[0][0];
+ pos++;
+
+ if (dd->ndim == 3 && d == 0)
+ {
+ buf_s[pos] = comm->zone_d2[0][1];
+ pos++;
+ buf_s[pos] = comm->zone_d1[0];
+ pos++;
+ }
+
+ /* We only need to communicate the extremes
+ * in the forward direction
+ */
+ npulse = comm->cd[d].np;
+ if (bPBC)
+ {
+ /* Take the minimum to avoid double communication */
+ npulse_min = min(npulse,dd->nc[dim]-1-npulse);
+ }
+ else
+ {
+ /* Without PBC we should really not communicate over
+ * the boundaries, but implementing that complicates
+ * the communication setup and therefore we simply
+ * do all communication, but ignore some data.
+ */
+ npulse_min = npulse;
+ }
+ for(p=0; p<npulse_min; p++)
+ {
+ /* Communicate the extremes forward */
+ bUse = (bPBC || dd->ci[dim] > 0);
+
+ dd_sendrecv_rvec(dd, d, dddirForward,
+ extr_s+d, dd->ndim-d-1,
+ extr_r+d, dd->ndim-d-1);
+
+ if (bUse)
+ {
+ for(d1=d; d1<dd->ndim-1; d1++)
+ {
+ extr_s[d1][0] = max(extr_s[d1][0],extr_r[d1][0]);
+ extr_s[d1][1] = min(extr_s[d1][1],extr_r[d1][1]);
+ }
+ }
+ }
+
+ buf_size = pos;
+ for(p=0; p<npulse; p++)
+ {
+ /* Communicate all the zone information backward */
+ bUse = (bPBC || dd->ci[dim] < dd->nc[dim] - 1);
+
+ dd_sendrecv_ddzone(dd, d, dddirBackward,
+ buf_s, buf_size,
+ buf_r, buf_size);
+
+ clear_rvec(dh);
+ if (p > 0)
+ {
+ for(d1=d+1; d1<dd->ndim; d1++)
+ {
+ /* Determine the decrease of maximum required
+ * communication height along d1 due to the distance along d,
+ * this avoids a lot of useless atom communication.
+ */
+ dist_d = comm->cell_x1[dim] - buf_r[0].p1_0;
+
+ if (ddbox->tric_dir[dim])
+ {
+ /* c is the off-diagonal coupling between the cell planes
+ * along directions d and d1.
+ */
+ c = ddbox->v[dim][dd->dim[d1]][dim];
+ }
+ else
+ {
+ c = 0;
+ }
+ det = (1 + c*c)*comm->cutoff*comm->cutoff - dist_d*dist_d;
+ if (det > 0)
+ {
+ dh[d1] = comm->cutoff - (c*dist_d + sqrt(det))/(1 + c*c);
+ }
+ else
+ {
+ /* A negative value signals out of range */
+ dh[d1] = -1;
+ }
+ }
+ }
+
+ /* Accumulate the extremes over all pulses */
+ for(i=0; i<buf_size; i++)
+ {
+ if (p == 0)
+ {
+ buf_e[i] = buf_r[i];
+ }
+ else
+ {
+ if (bUse)
+ {
+ buf_e[i].min0 = min(buf_e[i].min0,buf_r[i].min0);
+ buf_e[i].max1 = max(buf_e[i].max1,buf_r[i].max1);
+ }
+
+ if (dd->ndim == 3 && d == 0 && i == buf_size - 1)
+ {
+ d1 = 1;
+ }
+ else
+ {
+ d1 = d + 1;
+ }
+ if (bUse && dh[d1] >= 0)
+ {
+ buf_e[i].mch0 = max(buf_e[i].mch0,buf_r[i].mch0-dh[d1]);
+ buf_e[i].mch1 = max(buf_e[i].mch1,buf_r[i].mch1-dh[d1]);
+ }
+ }
+ /* Copy the received buffer to the send buffer,
+ * to pass the data through with the next pulse.
+ */
+ buf_s[i] = buf_r[i];
+ }
+ if (((bPBC || dd->ci[dim]+npulse < dd->nc[dim]) && p == npulse-1) ||
+ (!bPBC && dd->ci[dim]+1+p == dd->nc[dim]-1))
+ {
+ /* Store the extremes */
+ pos = 0;
+
+ for(d1=d; d1<dd->ndim-1; d1++)
+ {
+ extr_s[d1][1] = min(extr_s[d1][1],buf_e[pos].min0);
+ extr_s[d1][0] = max(extr_s[d1][0],buf_e[pos].max1);
+ pos++;
+ }
+
+ if (d == 1 || (d == 0 && dd->ndim == 3))
+ {
+ for(i=d; i<2; i++)
+ {
+ comm->zone_d2[1-d][i] = buf_e[pos];
+ pos++;
+ }
+ }
+ if (d == 0)
+ {
+ comm->zone_d1[1] = buf_e[pos];
+ pos++;
+ }
+ }
+ }
+ }
+
+ if (dd->ndim >= 2)
+ {
+ dim = dd->dim[1];
+ for(i=0; i<2; i++)
+ {
+ if (debug)
+ {
+ print_ddzone(debug,1,i,0,&comm->zone_d1[i]);
+ }
+ cell_ns_x0[dim] = min(cell_ns_x0[dim],comm->zone_d1[i].min0);
+ cell_ns_x1[dim] = max(cell_ns_x1[dim],comm->zone_d1[i].max1);
+ }
+ }
+ if (dd->ndim >= 3)
+ {
+ dim = dd->dim[2];
+ for(i=0; i<2; i++)
+ {
+ for(j=0; j<2; j++)
+ {
+ if (debug)
+ {
+ print_ddzone(debug,2,i,j,&comm->zone_d2[i][j]);
+ }
+ cell_ns_x0[dim] = min(cell_ns_x0[dim],comm->zone_d2[i][j].min0);
+ cell_ns_x1[dim] = max(cell_ns_x1[dim],comm->zone_d2[i][j].max1);
+ }
+ }
+ }
+ for(d=1; d<dd->ndim; d++)
+ {
+ comm->cell_f_max0[d] = extr_s[d-1][0];
+ comm->cell_f_min1[d] = extr_s[d-1][1];
+ if (debug)
+ {
+ fprintf(debug,"Cell fraction d %d, max0 %f, min1 %f\n",
+ d,comm->cell_f_max0[d],comm->cell_f_min1[d]);
+ }
+ }
+}
+
+static void dd_collect_cg(gmx_domdec_t *dd,
+ t_state *state_local)
+{
+ gmx_domdec_master_t *ma=NULL;
+ int buf2[2],*ibuf,i,ncg_home=0,*cg=NULL,nat_home=0;
+ t_block *cgs_gl;
+
+ if (state_local->ddp_count == dd->comm->master_cg_ddp_count)
+ {
+ /* The master has the correct distribution */
+ return;
+ }
+
+ if (state_local->ddp_count == dd->ddp_count)
+ {
+ ncg_home = dd->ncg_home;
+ cg = dd->index_gl;
+ nat_home = dd->nat_home;
+ }
+ else if (state_local->ddp_count_cg_gl == state_local->ddp_count)
+ {
+ cgs_gl = &dd->comm->cgs_gl;
+
+ ncg_home = state_local->ncg_gl;
+ cg = state_local->cg_gl;
+ nat_home = 0;
+ for(i=0; i<ncg_home; i++)
+ {
+ nat_home += cgs_gl->index[cg[i]+1] - cgs_gl->index[cg[i]];
+ }
+ }
+ else
+ {
+ gmx_incons("Attempted to collect a vector for a state for which the charge group distribution is unknown");
+ }
+
+ buf2[0] = dd->ncg_home;
+ buf2[1] = dd->nat_home;
+ if (DDMASTER(dd))
+ {
+ ma = dd->ma;
+ ibuf = ma->ibuf;
+ }
+ else
+ {
+ ibuf = NULL;
+ }
+ /* Collect the charge group and atom counts on the master */
+ dd_gather(dd,2*sizeof(int),buf2,ibuf);
+
+ if (DDMASTER(dd))
+ {
+ ma->index[0] = 0;
+ for(i=0; i<dd->nnodes; i++)
+ {
+ ma->ncg[i] = ma->ibuf[2*i];
+ ma->nat[i] = ma->ibuf[2*i+1];
+ ma->index[i+1] = ma->index[i] + ma->ncg[i];
+
+ }
+ /* Make byte counts and indices */
+ for(i=0; i<dd->nnodes; i++)
+ {
+ ma->ibuf[i] = ma->ncg[i]*sizeof(int);
+ ma->ibuf[dd->nnodes+i] = ma->index[i]*sizeof(int);
+ }
+ if (debug)
+ {
+ fprintf(debug,"Initial charge group distribution: ");
+ for(i=0; i<dd->nnodes; i++)
+ fprintf(debug," %d",ma->ncg[i]);
+ fprintf(debug,"\n");
+ }
+ }
+
+ /* Collect the charge group indices on the master */
+ dd_gatherv(dd,
+ dd->ncg_home*sizeof(int),dd->index_gl,
+ DDMASTER(dd) ? ma->ibuf : NULL,
+ DDMASTER(dd) ? ma->ibuf+dd->nnodes : NULL,
+ DDMASTER(dd) ? ma->cg : NULL);
+
+ dd->comm->master_cg_ddp_count = state_local->ddp_count;
+}
+
+static void dd_collect_vec_sendrecv(gmx_domdec_t *dd,
+ rvec *lv,rvec *v)
+{
+ gmx_domdec_master_t *ma;
+ int n,i,c,a,nalloc=0;
+ rvec *buf=NULL;
+ t_block *cgs_gl;
+
+ ma = dd->ma;
+
+ if (!DDMASTER(dd))
+ {
+#ifdef GMX_MPI
+ MPI_Send(lv,dd->nat_home*sizeof(rvec),MPI_BYTE,DDMASTERRANK(dd),
+ dd->rank,dd->mpi_comm_all);
+#endif
+ } else {
+ /* Copy the master coordinates to the global array */
+ cgs_gl = &dd->comm->cgs_gl;
+
+ n = DDMASTERRANK(dd);
+ a = 0;
+ for(i=ma->index[n]; i<ma->index[n+1]; i++)
+ {
+ for(c=cgs_gl->index[ma->cg[i]]; c<cgs_gl->index[ma->cg[i]+1]; c++)
+ {
+ copy_rvec(lv[a++],v[c]);
+ }
+ }
+
+ for(n=0; n<dd->nnodes; n++)
+ {
+ if (n != dd->rank)
+ {
+ if (ma->nat[n] > nalloc)
+ {
+ nalloc = over_alloc_dd(ma->nat[n]);
+ srenew(buf,nalloc);
+ }
+#ifdef GMX_MPI
+ MPI_Recv(buf,ma->nat[n]*sizeof(rvec),MPI_BYTE,DDRANK(dd,n),
+ n,dd->mpi_comm_all,MPI_STATUS_IGNORE);
+#endif
+ a = 0;
+ for(i=ma->index[n]; i<ma->index[n+1]; i++)
+ {
+ for(c=cgs_gl->index[ma->cg[i]]; c<cgs_gl->index[ma->cg[i]+1]; c++)
+ {
+ copy_rvec(buf[a++],v[c]);
+ }
+ }
+ }
+ }
+ sfree(buf);
+ }
+}
+
+static void get_commbuffer_counts(gmx_domdec_t *dd,
+ int **counts,int **disps)
+{
+ gmx_domdec_master_t *ma;
+ int n;
+
+ ma = dd->ma;
+
+ /* Make the rvec count and displacment arrays */
+ *counts = ma->ibuf;
+ *disps = ma->ibuf + dd->nnodes;
+ for(n=0; n<dd->nnodes; n++)
+ {
+ (*counts)[n] = ma->nat[n]*sizeof(rvec);
+ (*disps)[n] = (n == 0 ? 0 : (*disps)[n-1] + (*counts)[n-1]);
+ }
+}
+
+static void dd_collect_vec_gatherv(gmx_domdec_t *dd,
+ rvec *lv,rvec *v)
+{
+ gmx_domdec_master_t *ma;
+ int *rcounts=NULL,*disps=NULL;
+ int n,i,c,a;
+ rvec *buf=NULL;
+ t_block *cgs_gl;
+
+ ma = dd->ma;
+
+ if (DDMASTER(dd))
+ {
+ get_commbuffer_counts(dd,&rcounts,&disps);
+
+ buf = ma->vbuf;
+ }
+
+ dd_gatherv(dd,dd->nat_home*sizeof(rvec),lv,rcounts,disps,buf);
+
+ if (DDMASTER(dd))
+ {
+ cgs_gl = &dd->comm->cgs_gl;
+
+ a = 0;
+ for(n=0; n<dd->nnodes; n++)
+ {
+ for(i=ma->index[n]; i<ma->index[n+1]; i++)
+ {
+ for(c=cgs_gl->index[ma->cg[i]]; c<cgs_gl->index[ma->cg[i]+1]; c++)
+ {
+ copy_rvec(buf[a++],v[c]);
+ }
+ }
+ }
+ }
+}
+
+void dd_collect_vec(gmx_domdec_t *dd,
+ t_state *state_local,rvec *lv,rvec *v)
+{
+ gmx_domdec_master_t *ma;
+ int n,i,c,a,nalloc=0;
+ rvec *buf=NULL;
+
+ dd_collect_cg(dd,state_local);
+
+ if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
+ {
+ dd_collect_vec_sendrecv(dd,lv,v);
+ }
+ else
+ {
+ dd_collect_vec_gatherv(dd,lv,v);
+ }
+}
+
+
+void dd_collect_state(gmx_domdec_t *dd,
+ t_state *state_local,t_state *state)
+{
+ int est,i,j,nh;
+
+ nh = state->nhchainlength;
+
+ if (DDMASTER(dd))
+ {
+ state->lambda = state_local->lambda;
+ state->veta = state_local->veta;
+ state->vol0 = state_local->vol0;
+ copy_mat(state_local->box,state->box);
+ copy_mat(state_local->boxv,state->boxv);
+ copy_mat(state_local->svir_prev,state->svir_prev);
+ copy_mat(state_local->fvir_prev,state->fvir_prev);
+ copy_mat(state_local->pres_prev,state->pres_prev);
+
+
+ for(i=0; i<state_local->ngtc; i++)
+ {
+ for(j=0; j<nh; j++) {
+ state->nosehoover_xi[i*nh+j] = state_local->nosehoover_xi[i*nh+j];
+ state->nosehoover_vxi[i*nh+j] = state_local->nosehoover_vxi[i*nh+j];
+ }
+ state->therm_integral[i] = state_local->therm_integral[i];
+ }
+ for(i=0; i<state_local->nnhpres; i++)
+ {
+ for(j=0; j<nh; j++) {
+ state->nhpres_xi[i*nh+j] = state_local->nhpres_xi[i*nh+j];
+ state->nhpres_vxi[i*nh+j] = state_local->nhpres_vxi[i*nh+j];
+ }
+ }
+ }
+ for(est=0; est<estNR; est++)
+ {
+ if (EST_DISTR(est) && state_local->flags & (1<<est))
+ {
+ switch (est) {
+ case estX:
+ dd_collect_vec(dd,state_local,state_local->x,state->x);
+ break;
+ case estV:
+ dd_collect_vec(dd,state_local,state_local->v,state->v);
+ break;
+ case estSDX:
+ dd_collect_vec(dd,state_local,state_local->sd_X,state->sd_X);
+ break;
+ case estCGP:
+ dd_collect_vec(dd,state_local,state_local->cg_p,state->cg_p);
+ break;
+ case estLD_RNG:
+ if (state->nrngi == 1)
+ {
+ if (DDMASTER(dd))
+ {
+ for(i=0; i<state_local->nrng; i++)
+ {
+ state->ld_rng[i] = state_local->ld_rng[i];
+ }
+ }
+ }
+ else
+ {
+ dd_gather(dd,state_local->nrng*sizeof(state->ld_rng[0]),
+ state_local->ld_rng,state->ld_rng);
+ }
+ break;
+ case estLD_RNGI:
+ if (state->nrngi == 1)
+ {
+ if (DDMASTER(dd))
+ {
+ state->ld_rngi[0] = state_local->ld_rngi[0];
+ }
+ }
+ else
+ {
+ dd_gather(dd,sizeof(state->ld_rngi[0]),
+ state_local->ld_rngi,state->ld_rngi);
+ }
+ break;
+ case estDISRE_INITF:
+ case estDISRE_RM3TAV:
+ case estORIRE_INITF:
+ case estORIRE_DTAV:
+ break;
+ default:
+ gmx_incons("Unknown state entry encountered in dd_collect_state");
+ }
+ }
+ }
+}
+
+static void dd_realloc_fr_cg(t_forcerec *fr,int nalloc)
+{
+ if (debug)
+ {
+ fprintf(debug,"Reallocating forcerec: currently %d, required %d, allocating %d\n",fr->cg_nalloc,nalloc,over_alloc_dd(nalloc));
+ }
+ fr->cg_nalloc = over_alloc_dd(nalloc);
+ srenew(fr->cg_cm,fr->cg_nalloc);
+ srenew(fr->cginfo,fr->cg_nalloc);
+}
+
+static void dd_realloc_state(t_state *state,rvec **f,int nalloc)
+{
+ int est;
+
+ if (debug)
+ {
+ fprintf(debug,"Reallocating state: currently %d, required %d, allocating %d\n",state->nalloc,nalloc,over_alloc_dd(nalloc));
+ }
+
+ state->nalloc = over_alloc_dd(nalloc);
+
+ for(est=0; est<estNR; est++)
+ {
+ if (EST_DISTR(est) && state->flags & (1<<est))
+ {
+ switch(est) {
+ case estX:
+ srenew(state->x,state->nalloc);
+ break;
+ case estV:
+ srenew(state->v,state->nalloc);
+ break;
+ case estSDX:
+ srenew(state->sd_X,state->nalloc);
+ break;
+ case estCGP:
+ srenew(state->cg_p,state->nalloc);
+ break;
+ case estLD_RNG:
+ case estLD_RNGI:
+ case estDISRE_INITF:
+ case estDISRE_RM3TAV:
+ case estORIRE_INITF:
+ case estORIRE_DTAV:
+ /* No reallocation required */
+ break;
+ default:
+ gmx_incons("Unknown state entry encountered in dd_realloc_state");
+ }
+ }
+ }
+
+ if (f != NULL)
+ {
+ srenew(*f,state->nalloc);
+ }
+}
+
+static void dd_distribute_vec_sendrecv(gmx_domdec_t *dd,t_block *cgs,
+ rvec *v,rvec *lv)
+{
+ gmx_domdec_master_t *ma;
+ int n,i,c,a,nalloc=0;
+ rvec *buf=NULL;
+
+ if (DDMASTER(dd))
+ {
+ ma = dd->ma;
+
+ for(n=0; n<dd->nnodes; n++)
+ {
+ if (n != dd->rank)
+ {
+ if (ma->nat[n] > nalloc)
+ {
+ nalloc = over_alloc_dd(ma->nat[n]);
+ srenew(buf,nalloc);
+ }
+ /* Use lv as a temporary buffer */
+ a = 0;
+ for(i=ma->index[n]; i<ma->index[n+1]; i++)
+ {
+ for(c=cgs->index[ma->cg[i]]; c<cgs->index[ma->cg[i]+1]; c++)
+ {
+ copy_rvec(v[c],buf[a++]);
+ }
+ }
+ if (a != ma->nat[n])
+ {
+ gmx_fatal(FARGS,"Internal error a (%d) != nat (%d)",
+ a,ma->nat[n]);
+ }
+
+#ifdef GMX_MPI
+ MPI_Send(buf,ma->nat[n]*sizeof(rvec),MPI_BYTE,
+ DDRANK(dd,n),n,dd->mpi_comm_all);
+#endif
+ }
+ }
+ sfree(buf);
+ n = DDMASTERRANK(dd);
+ a = 0;
+ for(i=ma->index[n]; i<ma->index[n+1]; i++)
+ {
+ for(c=cgs->index[ma->cg[i]]; c<cgs->index[ma->cg[i]+1]; c++)
+ {
+ copy_rvec(v[c],lv[a++]);
+ }
+ }
+ }
+ else
+ {
+#ifdef GMX_MPI
+ MPI_Recv(lv,dd->nat_home*sizeof(rvec),MPI_BYTE,DDMASTERRANK(dd),
+ MPI_ANY_TAG,dd->mpi_comm_all,MPI_STATUS_IGNORE);
+#endif
+ }
+}
+
+static void dd_distribute_vec_scatterv(gmx_domdec_t *dd,t_block *cgs,
+ rvec *v,rvec *lv)
+{
+ gmx_domdec_master_t *ma;
+ int *scounts=NULL,*disps=NULL;
+ int n,i,c,a,nalloc=0;
+ rvec *buf=NULL;
+
+ if (DDMASTER(dd))
+ {
+ ma = dd->ma;
+
+ get_commbuffer_counts(dd,&scounts,&disps);
+
+ buf = ma->vbuf;
+ a = 0;
+ for(n=0; n<dd->nnodes; n++)
+ {
+ for(i=ma->index[n]; i<ma->index[n+1]; i++)
+ {
+ for(c=cgs->index[ma->cg[i]]; c<cgs->index[ma->cg[i]+1]; c++)
+ {
+ copy_rvec(v[c],buf[a++]);
+ }
+ }
+ }
+ }
+
+ dd_scatterv(dd,scounts,disps,buf,dd->nat_home*sizeof(rvec),lv);
+}
+
+static void dd_distribute_vec(gmx_domdec_t *dd,t_block *cgs,rvec *v,rvec *lv)
+{
+ if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
+ {
+ dd_distribute_vec_sendrecv(dd,cgs,v,lv);
+ }
+ else
+ {
+ dd_distribute_vec_scatterv(dd,cgs,v,lv);
+ }
+}
+
+static void dd_distribute_state(gmx_domdec_t *dd,t_block *cgs,
+ t_state *state,t_state *state_local,
+ rvec **f)
+{
+ int i,j,ngtch,ngtcp,nh;
+
+ nh = state->nhchainlength;
+
+ if (DDMASTER(dd))
+ {
+ state_local->lambda = state->lambda;
+ state_local->veta = state->veta;
+ state_local->vol0 = state->vol0;
+ copy_mat(state->box,state_local->box);
+ copy_mat(state->box_rel,state_local->box_rel);
+ copy_mat(state->boxv,state_local->boxv);
+ copy_mat(state->svir_prev,state_local->svir_prev);
+ copy_mat(state->fvir_prev,state_local->fvir_prev);
+ for(i=0; i<state_local->ngtc; i++)
+ {
+ for(j=0; j<nh; j++) {
+ state_local->nosehoover_xi[i*nh+j] = state->nosehoover_xi[i*nh+j];
+ state_local->nosehoover_vxi[i*nh+j] = state->nosehoover_vxi[i*nh+j];
+ }
+ state_local->therm_integral[i] = state->therm_integral[i];
+ }
+ for(i=0; i<state_local->nnhpres; i++)
+ {
+ for(j=0; j<nh; j++) {
+ state_local->nhpres_xi[i*nh+j] = state->nhpres_xi[i*nh+j];
+ state_local->nhpres_vxi[i*nh+j] = state->nhpres_vxi[i*nh+j];
+ }
+ }
+ }
+ dd_bcast(dd,sizeof(real),&state_local->lambda);
+ dd_bcast(dd,sizeof(real),&state_local->veta);
+ dd_bcast(dd,sizeof(real),&state_local->vol0);
+ dd_bcast(dd,sizeof(state_local->box),state_local->box);
+ dd_bcast(dd,sizeof(state_local->box_rel),state_local->box_rel);
+ dd_bcast(dd,sizeof(state_local->boxv),state_local->boxv);
+ dd_bcast(dd,sizeof(state_local->svir_prev),state_local->svir_prev);
+ dd_bcast(dd,sizeof(state_local->fvir_prev),state_local->fvir_prev);
+ dd_bcast(dd,((state_local->ngtc*nh)*sizeof(double)),state_local->nosehoover_xi);
+ dd_bcast(dd,((state_local->ngtc*nh)*sizeof(double)),state_local->nosehoover_vxi);
+ dd_bcast(dd,state_local->ngtc*sizeof(double),state_local->therm_integral);
+ dd_bcast(dd,((state_local->nnhpres*nh)*sizeof(double)),state_local->nhpres_xi);
+ dd_bcast(dd,((state_local->nnhpres*nh)*sizeof(double)),state_local->nhpres_vxi);
+
+ if (dd->nat_home > state_local->nalloc)
+ {
+ dd_realloc_state(state_local,f,dd->nat_home);
+ }
+ for(i=0; i<estNR; i++)
+ {
+ if (EST_DISTR(i) && state_local->flags & (1<<i))
+ {
+ switch (i) {
+ case estX:
+ dd_distribute_vec(dd,cgs,state->x,state_local->x);
+ break;
+ case estV:
+ dd_distribute_vec(dd,cgs,state->v,state_local->v);
+ break;
+ case estSDX:
+ dd_distribute_vec(dd,cgs,state->sd_X,state_local->sd_X);
+ break;
+ case estCGP:
+ dd_distribute_vec(dd,cgs,state->cg_p,state_local->cg_p);
+ break;
+ case estLD_RNG:
+ if (state->nrngi == 1)
+ {
+ dd_bcastc(dd,
+ state_local->nrng*sizeof(state_local->ld_rng[0]),
+ state->ld_rng,state_local->ld_rng);
+ }
+ else
+ {
+ dd_scatter(dd,
+ state_local->nrng*sizeof(state_local->ld_rng[0]),
+ state->ld_rng,state_local->ld_rng);
+ }
+ break;
+ case estLD_RNGI:
+ if (state->nrngi == 1)
+ {
+ dd_bcastc(dd,sizeof(state_local->ld_rngi[0]),
+ state->ld_rngi,state_local->ld_rngi);
+ }
+ else
+ {
+ dd_scatter(dd,sizeof(state_local->ld_rngi[0]),
+ state->ld_rngi,state_local->ld_rngi);
+ }
+ break;
+ case estDISRE_INITF:
+ case estDISRE_RM3TAV:
+ case estORIRE_INITF:
+ case estORIRE_DTAV:
+ /* Not implemented yet */
+ break;
+ default:
+ gmx_incons("Unknown state entry encountered in dd_distribute_state");
+ }
+ }
+ }
+}
+
+static char dim2char(int dim)
+{
+ char c='?';
+
+ switch (dim)
+ {
+ case XX: c = 'X'; break;
+ case YY: c = 'Y'; break;
+ case ZZ: c = 'Z'; break;
+ default: gmx_fatal(FARGS,"Unknown dim %d",dim);
+ }
+
+ return c;
+}
+
+static void write_dd_grid_pdb(const char *fn,gmx_large_int_t step,
+ gmx_domdec_t *dd,matrix box,gmx_ddbox_t *ddbox)
+{
+ rvec grid_s[2],*grid_r=NULL,cx,r;
+ char fname[STRLEN],format[STRLEN],buf[22];
+ FILE *out;
+ int a,i,d,z,y,x;
+ matrix tric;
+ real vol;
+
+ copy_rvec(dd->comm->cell_x0,grid_s[0]);
+ copy_rvec(dd->comm->cell_x1,grid_s[1]);
+
+ if (DDMASTER(dd))
+ {
+ snew(grid_r,2*dd->nnodes);
+ }
+
+ dd_gather(dd,2*sizeof(rvec),grid_s[0],DDMASTER(dd) ? grid_r[0] : NULL);
+
+ if (DDMASTER(dd))
+ {
+ for(d=0; d<DIM; d++)
+ {
+ for(i=0; i<DIM; i++)
+ {
+ if (d == i)
+ {
+ tric[d][i] = 1;
+ }
+ else
+ {
+ if (dd->nc[d] > 1 && d < ddbox->npbcdim)
+ {
+ tric[d][i] = box[i][d]/box[i][i];
+ }
+ else
+ {
+ tric[d][i] = 0;
+ }
+ }
+ }
+ }
+ sprintf(fname,"%s_%s.pdb",fn,gmx_step_str(step,buf));
+ sprintf(format,"%s%s\n",pdbformat,"%6.2f%6.2f");
+ out = gmx_fio_fopen(fname,"w");
+ gmx_write_pdb_box(out,dd->bScrewPBC ? epbcSCREW : epbcXYZ,box);
+ a = 1;
+ for(i=0; i<dd->nnodes; i++)
+ {
+ vol = dd->nnodes/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
+ for(d=0; d<DIM; d++)
+ {
+ vol *= grid_r[i*2+1][d] - grid_r[i*2][d];
+ }
+ for(z=0; z<2; z++)
+ {
+ for(y=0; y<2; y++)
+ {
+ for(x=0; x<2; x++)
+ {
+ cx[XX] = grid_r[i*2+x][XX];
+ cx[YY] = grid_r[i*2+y][YY];
+ cx[ZZ] = grid_r[i*2+z][ZZ];
+ mvmul(tric,cx,r);
+ fprintf(out,format,"ATOM",a++,"CA","GLY",' ',1+i,
+ 10*r[XX],10*r[YY],10*r[ZZ],1.0,vol);
+ }
+ }
+ }
+ for(d=0; d<DIM; d++)
+ {
+ for(x=0; x<4; x++)
+ {
+ switch(d)
+ {
+ case 0: y = 1 + i*8 + 2*x; break;
+ case 1: y = 1 + i*8 + 2*x - (x % 2); break;
+ case 2: y = 1 + i*8 + x; break;
+ }
+ fprintf(out,"%6s%5d%5d\n","CONECT",y,y+(1<<d));
+ }
+ }
+ }
+ gmx_fio_fclose(out);
+ sfree(grid_r);
+ }
+}
+
+void write_dd_pdb(const char *fn,gmx_large_int_t step,const char *title,
+ gmx_mtop_t *mtop,t_commrec *cr,
+ int natoms,rvec x[],matrix box)
+{
+ char fname[STRLEN],format[STRLEN],format4[STRLEN],buf[22];
+ FILE *out;
+ int i,ii,resnr,c;
+ char *atomname,*resname;
+ real b;
+ gmx_domdec_t *dd;
+
+ dd = cr->dd;
+ if (natoms == -1)
+ {
+ natoms = dd->comm->nat[ddnatVSITE];
+ }
+
+ sprintf(fname,"%s_%s_n%d.pdb",fn,gmx_step_str(step,buf),cr->sim_nodeid);
+
+ sprintf(format,"%s%s\n",pdbformat,"%6.2f%6.2f");
+ sprintf(format4,"%s%s\n",pdbformat4,"%6.2f%6.2f");
+
+ out = gmx_fio_fopen(fname,"w");
+
+ fprintf(out,"TITLE %s\n",title);
+ gmx_write_pdb_box(out,dd->bScrewPBC ? epbcSCREW : epbcXYZ,box);
+ for(i=0; i<natoms; i++)
+ {
+ ii = dd->gatindex[i];
+ gmx_mtop_atominfo_global(mtop,ii,&atomname,&resnr,&resname);
+ if (i < dd->comm->nat[ddnatZONE])
+ {
+ c = 0;
+ while (i >= dd->cgindex[dd->comm->zones.cg_range[c+1]])
+ {
+ c++;
+ }
+ b = c;
+ }
+ else if (i < dd->comm->nat[ddnatVSITE])
+ {
+ b = dd->comm->zones.n;
+ }
+ else
+ {
+ b = dd->comm->zones.n + 1;
+ }
+ fprintf(out,strlen(atomname)<4 ? format : format4,
+ "ATOM",(ii+1)%100000,
+ atomname,resname,' ',resnr%10000,' ',
+ 10*x[i][XX],10*x[i][YY],10*x[i][ZZ],1.0,b);
+ }
+ fprintf(out,"TER\n");
+
+ gmx_fio_fclose(out);
+}
+
+real dd_cutoff_mbody(gmx_domdec_t *dd)
+{
+ gmx_domdec_comm_t *comm;
+ int di;
+ real r;
+
+ comm = dd->comm;
+
+ r = -1;
+ if (comm->bInterCGBondeds)
+ {
+ if (comm->cutoff_mbody > 0)
+ {
+ r = comm->cutoff_mbody;
+ }
+ else
+ {
+ /* cutoff_mbody=0 means we do not have DLB */
+ r = comm->cellsize_min[dd->dim[0]];
+ for(di=1; di<dd->ndim; di++)
+ {
+ r = min(r,comm->cellsize_min[dd->dim[di]]);
+ }
+ if (comm->bBondComm)
+ {
+ r = max(r,comm->cutoff_mbody);
+ }
+ else
+ {
+ r = min(r,comm->cutoff);
+ }
+ }
+ }
+
+ return r;
+}
+
+real dd_cutoff_twobody(gmx_domdec_t *dd)
+{
+ real r_mb;
+
+ r_mb = dd_cutoff_mbody(dd);
+
+ return max(dd->comm->cutoff,r_mb);
+}
+
+
+static void dd_cart_coord2pmecoord(gmx_domdec_t *dd,ivec coord,ivec coord_pme)
+{
+ int nc,ntot;
+
+ nc = dd->nc[dd->comm->cartpmedim];
+ ntot = dd->comm->ntot[dd->comm->cartpmedim];
+ copy_ivec(coord,coord_pme);
+ coord_pme[dd->comm->cartpmedim] =
+ nc + (coord[dd->comm->cartpmedim]*(ntot - nc) + (ntot - nc)/2)/nc;
+}
+
+static int low_ddindex2pmeindex(int ndd,int npme,int ddindex)
+{
+ /* Here we assign a PME node to communicate with this DD node
+ * by assuming that the major index of both is x.
+ * We add cr->npmenodes/2 to obtain an even distribution.
+ */
+ return (ddindex*npme + npme/2)/ndd;
+}
+
+static int ddindex2pmeindex(const gmx_domdec_t *dd,int ddindex)
+{
+ return low_ddindex2pmeindex(dd->nnodes,dd->comm->npmenodes,ddindex);
+}
+
+static int cr_ddindex2pmeindex(const t_commrec *cr,int ddindex)
+{
+ return low_ddindex2pmeindex(cr->dd->nnodes,cr->npmenodes,ddindex);
+}
+
+static int *dd_pmenodes(t_commrec *cr)
+{
+ int *pmenodes;
+ int n,i,p0,p1;
+
+ snew(pmenodes,cr->npmenodes);
+ n = 0;
+ for(i=0; i<cr->dd->nnodes; i++) {
+ p0 = cr_ddindex2pmeindex(cr,i);
+ p1 = cr_ddindex2pmeindex(cr,i+1);
+ if (i+1 == cr->dd->nnodes || p1 > p0) {
+ if (debug)
+ fprintf(debug,"pmenode[%d] = %d\n",n,i+1+n);
+ pmenodes[n] = i + 1 + n;
+ n++;
+ }
+ }
+
+ return pmenodes;
+}
+
+static int gmx_ddcoord2pmeindex(t_commrec *cr,int x,int y,int z)
+{
+ gmx_domdec_t *dd;
+ ivec coords,coords_pme,nc;
+ int slab;
+
+ dd = cr->dd;
+ /*
+ if (dd->comm->bCartesian) {
+ gmx_ddindex2xyz(dd->nc,ddindex,coords);
+ dd_coords2pmecoords(dd,coords,coords_pme);
+ copy_ivec(dd->ntot,nc);
+ nc[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
+ coords_pme[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
+
+ slab = (coords_pme[XX]*nc[YY] + coords_pme[YY])*nc[ZZ] + coords_pme[ZZ];
+ } else {
+ slab = (ddindex*cr->npmenodes + cr->npmenodes/2)/dd->nnodes;
+ }
+ */
+ coords[XX] = x;
+ coords[YY] = y;
+ coords[ZZ] = z;
+ slab = ddindex2pmeindex(dd,dd_index(dd->nc,coords));
+
+ return slab;
+}
+
+static int ddcoord2simnodeid(t_commrec *cr,int x,int y,int z)
+{
+ gmx_domdec_comm_t *comm;
+ ivec coords;
+ int ddindex,nodeid=-1;
+
+ comm = cr->dd->comm;
+
+ coords[XX] = x;
+ coords[YY] = y;
+ coords[ZZ] = z;
+ if (comm->bCartesianPP_PME)
+ {
+#ifdef GMX_MPI
+ MPI_Cart_rank(cr->mpi_comm_mysim,coords,&nodeid);
+#endif
+ }
+ else
+ {
+ ddindex = dd_index(cr->dd->nc,coords);
+ if (comm->bCartesianPP)
+ {
+ nodeid = comm->ddindex2simnodeid[ddindex];
+ }
+ else
+ {
+ if (comm->pmenodes)
+ {
+ nodeid = ddindex + gmx_ddcoord2pmeindex(cr,x,y,z);
+ }
+ else
+ {
+ nodeid = ddindex;
+ }
+ }
+ }
+
+ return nodeid;
+}
+
+static int dd_simnode2pmenode(t_commrec *cr,int sim_nodeid)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ ivec coord,coord_pme;
+ int i;
+ int pmenode=-1;
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+ /* This assumes a uniform x domain decomposition grid cell size */
+ if (comm->bCartesianPP_PME)
+ {
+#ifdef GMX_MPI
+ MPI_Cart_coords(cr->mpi_comm_mysim,sim_nodeid,DIM,coord);
+ if (coord[comm->cartpmedim] < dd->nc[comm->cartpmedim])
+ {
+ /* This is a PP node */
+ dd_cart_coord2pmecoord(dd,coord,coord_pme);
+ MPI_Cart_rank(cr->mpi_comm_mysim,coord_pme,&pmenode);
+ }
+#endif
+ }
+ else if (comm->bCartesianPP)
+ {
+ if (sim_nodeid < dd->nnodes)
+ {
+ pmenode = dd->nnodes + ddindex2pmeindex(dd,sim_nodeid);
+ }
+ }
+ else
+ {
+ /* This assumes DD cells with identical x coordinates
+ * are numbered sequentially.
+ */
+ if (dd->comm->pmenodes == NULL)
+ {
+ if (sim_nodeid < dd->nnodes)
+ {
+ /* The DD index equals the nodeid */
+ pmenode = dd->nnodes + ddindex2pmeindex(dd,sim_nodeid);
+ }
+ }
+ else
+ {
+ i = 0;
+ while (sim_nodeid > dd->comm->pmenodes[i])
+ {
+ i++;
+ }
+ if (sim_nodeid < dd->comm->pmenodes[i])
+ {
+ pmenode = dd->comm->pmenodes[i];
+ }
+ }
+ }
+
+ return pmenode;
+}
+
+gmx_bool gmx_pmeonlynode(t_commrec *cr,int sim_nodeid)
+{
+ gmx_bool bPMEOnlyNode;
+
+ if (DOMAINDECOMP(cr))
+ {
+ bPMEOnlyNode = (dd_simnode2pmenode(cr,sim_nodeid) == -1);
+ }
+ else
+ {
+ bPMEOnlyNode = FALSE;
+ }
+
+ return bPMEOnlyNode;
+}
+
+void get_pme_ddnodes(t_commrec *cr,int pmenodeid,
+ int *nmy_ddnodes,int **my_ddnodes,int *node_peer)
+{
+ gmx_domdec_t *dd;
+ int x,y,z;
+ ivec coord,coord_pme;
+
+ dd = cr->dd;
+
+ snew(*my_ddnodes,(dd->nnodes+cr->npmenodes-1)/cr->npmenodes);
+
+ *nmy_ddnodes = 0;
+ for(x=0; x<dd->nc[XX]; x++)
+ {
+ for(y=0; y<dd->nc[YY]; y++)
+ {
+ for(z=0; z<dd->nc[ZZ]; z++)
+ {
+ if (dd->comm->bCartesianPP_PME)
+ {
+ coord[XX] = x;
+ coord[YY] = y;
+ coord[ZZ] = z;
+ dd_cart_coord2pmecoord(dd,coord,coord_pme);
+ if (dd->ci[XX] == coord_pme[XX] &&
+ dd->ci[YY] == coord_pme[YY] &&
+ dd->ci[ZZ] == coord_pme[ZZ])
+ (*my_ddnodes)[(*nmy_ddnodes)++] = ddcoord2simnodeid(cr,x,y,z);
+ }
+ else
+ {
+ /* The slab corresponds to the nodeid in the PME group */
+ if (gmx_ddcoord2pmeindex(cr,x,y,z) == pmenodeid)
+ {
+ (*my_ddnodes)[(*nmy_ddnodes)++] = ddcoord2simnodeid(cr,x,y,z);
+ }
+ }
+ }
+ }
+ }
+
+ /* The last PP-only node is the peer node */
+ *node_peer = (*my_ddnodes)[*nmy_ddnodes-1];
+
+ if (debug)
+ {
+ fprintf(debug,"Receive coordinates from PP nodes:");
+ for(x=0; x<*nmy_ddnodes; x++)
+ {
+ fprintf(debug," %d",(*my_ddnodes)[x]);
+ }
+ fprintf(debug,"\n");
+ }
+}
+
+static gmx_bool receive_vir_ener(t_commrec *cr)
+{
+ gmx_domdec_comm_t *comm;
+ int pmenode,coords[DIM],rank;
+ gmx_bool bReceive;
+
+ bReceive = TRUE;
+ if (cr->npmenodes < cr->dd->nnodes)
+ {
+ comm = cr->dd->comm;
+ if (comm->bCartesianPP_PME)
+ {
+ pmenode = dd_simnode2pmenode(cr,cr->sim_nodeid);
+#ifdef GMX_MPI
+ MPI_Cart_coords(cr->mpi_comm_mysim,cr->sim_nodeid,DIM,coords);
+ coords[comm->cartpmedim]++;
+ if (coords[comm->cartpmedim] < cr->dd->nc[comm->cartpmedim])
+ {
+ MPI_Cart_rank(cr->mpi_comm_mysim,coords,&rank);
+ if (dd_simnode2pmenode(cr,rank) == pmenode)
+ {
+ /* This is not the last PP node for pmenode */
+ bReceive = FALSE;
+ }
+ }
+#endif
+ }
+ else
+ {
+ pmenode = dd_simnode2pmenode(cr,cr->sim_nodeid);
+ if (cr->sim_nodeid+1 < cr->nnodes &&
+ dd_simnode2pmenode(cr,cr->sim_nodeid+1) == pmenode)
+ {
+ /* This is not the last PP node for pmenode */
+ bReceive = FALSE;
+ }
+ }
+ }
+
+ return bReceive;
+}
+
+static void set_zones_ncg_home(gmx_domdec_t *dd)
+{
+ gmx_domdec_zones_t *zones;
+ int i;
+
+ zones = &dd->comm->zones;
+
+ zones->cg_range[0] = 0;
+ for(i=1; i<zones->n+1; i++)
+ {
+ zones->cg_range[i] = dd->ncg_home;
+ }
+}
+
+static void rebuild_cgindex(gmx_domdec_t *dd,int *gcgs_index,t_state *state)
+{
+ int nat,i,*ind,*dd_cg_gl,*cgindex,cg_gl;
+
+ ind = state->cg_gl;
+ dd_cg_gl = dd->index_gl;
+ cgindex = dd->cgindex;
+ nat = 0;
+ cgindex[0] = nat;
+ for(i=0; i<state->ncg_gl; i++)
+ {
+ cgindex[i] = nat;
+ cg_gl = ind[i];
+ dd_cg_gl[i] = cg_gl;
+ nat += gcgs_index[cg_gl+1] - gcgs_index[cg_gl];
+ }
+ cgindex[i] = nat;
+
+ dd->ncg_home = state->ncg_gl;
+ dd->nat_home = nat;
+
+ set_zones_ncg_home(dd);
+}
+
+static int ddcginfo(const cginfo_mb_t *cginfo_mb,int cg)
+{
+ while (cg >= cginfo_mb->cg_end)
+ {
+ cginfo_mb++;
+ }
+
+ return cginfo_mb->cginfo[(cg - cginfo_mb->cg_start) % cginfo_mb->cg_mod];
+}
+
+static void dd_set_cginfo(int *index_gl,int cg0,int cg1,
+ t_forcerec *fr,char *bLocalCG)
+{
+ cginfo_mb_t *cginfo_mb;
+ int *cginfo;
+ int cg;
+
+ if (fr != NULL)
+ {
+ cginfo_mb = fr->cginfo_mb;
+ cginfo = fr->cginfo;
+
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ cginfo[cg] = ddcginfo(cginfo_mb,index_gl[cg]);
+ }
+ }
+
+ if (bLocalCG != NULL)
+ {
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ bLocalCG[index_gl[cg]] = TRUE;
+ }
+ }
+}
+
+static void make_dd_indices(gmx_domdec_t *dd,int *gcgs_index,int cg_start)
+{
+ int nzone,zone,zone1,cg0,cg,cg_gl,a,a_gl;
+ int *zone2cg,*zone_ncg1,*index_gl,*gatindex;
+ gmx_ga2la_t *ga2la;
+ char *bLocalCG;
+
+ bLocalCG = dd->comm->bLocalCG;
+
+ if (dd->nat_tot > dd->gatindex_nalloc)
+ {
+ dd->gatindex_nalloc = over_alloc_dd(dd->nat_tot);
+ srenew(dd->gatindex,dd->gatindex_nalloc);
+ }
+
+ nzone = dd->comm->zones.n;
+ zone2cg = dd->comm->zones.cg_range;
+ zone_ncg1 = dd->comm->zone_ncg1;
+ index_gl = dd->index_gl;
+ gatindex = dd->gatindex;
+
+ if (zone2cg[1] != dd->ncg_home)
+ {
+ gmx_incons("dd->ncg_zone is not up to date");
+ }
+
+ /* Make the local to global and global to local atom index */
+ a = dd->cgindex[cg_start];
+ for(zone=0; zone<nzone; zone++)
+ {
+ if (zone == 0)
+ {
+ cg0 = cg_start;
+ }
+ else
+ {
+ cg0 = zone2cg[zone];
+ }
+ for(cg=cg0; cg<zone2cg[zone+1]; cg++)
+ {
+ zone1 = zone;
+ if (cg - cg0 >= zone_ncg1[zone])
+ {
+ /* Signal that this cg is from more than one zone away */
+ zone1 += nzone;
+ }
+ cg_gl = index_gl[cg];
+ for(a_gl=gcgs_index[cg_gl]; a_gl<gcgs_index[cg_gl+1]; a_gl++)
+ {
+ gatindex[a] = a_gl;
+ ga2la_set(dd->ga2la,a_gl,a,zone1);
+ a++;
+ }
+ }
+ }
+}
+
+static int check_bLocalCG(gmx_domdec_t *dd,int ncg_sys,const char *bLocalCG,
+ const char *where)
+{
+ int ncg,i,ngl,nerr;
+
+ nerr = 0;
+ if (bLocalCG == NULL)
+ {
+ return nerr;
+ }
+ for(i=0; i<dd->ncg_tot; i++)
+ {
+ if (!bLocalCG[dd->index_gl[i]])
+ {
+ fprintf(stderr,
+ "DD node %d, %s: cg %d, global cg %d is not marked in bLocalCG (ncg_home %d)\n",dd->rank,where,i+1,dd->index_gl[i]+1,dd->ncg_home);
+ nerr++;
+ }
+ }
+ ngl = 0;
+ for(i=0; i<ncg_sys; i++)
+ {
+ if (bLocalCG[i])
+ {
+ ngl++;
+ }
+ }
+ if (ngl != dd->ncg_tot)
+ {
+ fprintf(stderr,"DD node %d, %s: In bLocalCG %d cgs are marked as local, whereas there are %d\n",dd->rank,where,ngl,dd->ncg_tot);
+ nerr++;
+ }
+
+ return nerr;
+}
+
+static void check_index_consistency(gmx_domdec_t *dd,
+ int natoms_sys,int ncg_sys,
+ const char *where)
+{
+ int nerr,ngl,i,a,cell;
+ int *have;
+
+ nerr = 0;
+
+ if (dd->comm->DD_debug > 1)
+ {
+ snew(have,natoms_sys);
+ for(a=0; a<dd->nat_tot; a++)
+ {
+ if (have[dd->gatindex[a]] > 0)
+ {
+ fprintf(stderr,"DD node %d: global atom %d occurs twice: index %d and %d\n",dd->rank,dd->gatindex[a]+1,have[dd->gatindex[a]],a+1);
+ }
+ else
+ {
+ have[dd->gatindex[a]] = a + 1;
+ }
+ }
+ sfree(have);
+ }
+
+ snew(have,dd->nat_tot);
+
+ ngl = 0;
+ for(i=0; i<natoms_sys; i++)
+ {
+ if (ga2la_get(dd->ga2la,i,&a,&cell))
+ {
+ if (a >= dd->nat_tot)
+ {
+ fprintf(stderr,"DD node %d: global atom %d marked as local atom %d, which is larger than nat_tot (%d)\n",dd->rank,i+1,a+1,dd->nat_tot);
+ nerr++;
+ }
+ else
+ {
+ have[a] = 1;
+ if (dd->gatindex[a] != i)
+ {
+ fprintf(stderr,"DD node %d: global atom %d marked as local atom %d, which has global atom index %d\n",dd->rank,i+1,a+1,dd->gatindex[a]+1);
+ nerr++;
+ }
+ }
+ ngl++;
+ }
+ }
+ if (ngl != dd->nat_tot)
+ {
+ fprintf(stderr,
+ "DD node %d, %s: %d global atom indices, %d local atoms\n",
+ dd->rank,where,ngl,dd->nat_tot);
+ }
+ for(a=0; a<dd->nat_tot; a++)
+ {
+ if (have[a] == 0)
+ {
+ fprintf(stderr,
+ "DD node %d, %s: local atom %d, global %d has no global index\n",
+ dd->rank,where,a+1,dd->gatindex[a]+1);
+ }
+ }
+ sfree(have);
+
+ nerr += check_bLocalCG(dd,ncg_sys,dd->comm->bLocalCG,where);
+
+ if (nerr > 0) {
+ gmx_fatal(FARGS,"DD node %d, %s: %d atom/cg index inconsistencies",
+ dd->rank,where,nerr);
+ }
+}
+
+static void clear_dd_indices(gmx_domdec_t *dd,int cg_start,int a_start)
+{
+ int i;
+ char *bLocalCG;
+
+ if (a_start == 0)
+ {
+ /* Clear the whole list without searching */
+ ga2la_clear(dd->ga2la);
+ }
+ else
+ {
+ for(i=a_start; i<dd->nat_tot; i++)
+ {
+ ga2la_del(dd->ga2la,dd->gatindex[i]);
+ }
+ }
+
+ bLocalCG = dd->comm->bLocalCG;
+ if (bLocalCG)
+ {
+ for(i=cg_start; i<dd->ncg_tot; i++)
+ {
+ bLocalCG[dd->index_gl[i]] = FALSE;
+ }
+ }
+
+ dd_clear_local_vsite_indices(dd);
+
+ if (dd->constraints)
+ {
+ dd_clear_local_constraint_indices(dd);
+ }
+}
+
+static real grid_jump_limit(gmx_domdec_comm_t *comm,int dim_ind)
+{
+ real grid_jump_limit;
+
+ /* The distance between the boundaries of cells at distance
+ * x+-1,y+-1 or y+-1,z+-1 is limited by the cut-off restrictions
+ * and by the fact that cells should not be shifted by more than
+ * half their size, such that cg's only shift by one cell
+ * at redecomposition.
+ */
+ grid_jump_limit = comm->cellsize_limit;
+ if (!comm->bVacDLBNoLimit)
+ {
+ grid_jump_limit = max(grid_jump_limit,
+ comm->cutoff/comm->cd[dim_ind].np);
+ }
+
+ return grid_jump_limit;
+}
+
+static void check_grid_jump(gmx_large_int_t step,gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
+{
+ gmx_domdec_comm_t *comm;
+ int d,dim;
+ real limit,bfac;
+
+ comm = dd->comm;
+
+ for(d=1; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ limit = grid_jump_limit(comm,d);
+ bfac = ddbox->box_size[dim];
+ if (ddbox->tric_dir[dim])
+ {
+ bfac *= ddbox->skew_fac[dim];
+ }
+ if ((comm->cell_f1[d] - comm->cell_f_max0[d])*bfac < limit ||
+ (comm->cell_f0[d] - comm->cell_f_min1[d])*bfac > -limit)
+ {
+ char buf[22];
+ gmx_fatal(FARGS,"Step %s: The domain decomposition grid has shifted too much in the %c-direction around cell %d %d %d\n",
+ gmx_step_str(step,buf),
+ dim2char(dim),dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
+ }
+ }
+}
+
+static int dd_load_count(gmx_domdec_comm_t *comm)
+{
+ return (comm->eFlop ? comm->flop_n : comm->cycl_n[ddCyclF]);
+}
+
+static float dd_force_load(gmx_domdec_comm_t *comm)
+{
+ float load;
+
+ if (comm->eFlop)
+ {
+ load = comm->flop;
+ if (comm->eFlop > 1)
+ {
+ load *= 1.0 + (comm->eFlop - 1)*(0.1*rand()/RAND_MAX - 0.05);
+ }
+ }
+ else
+ {
+ load = comm->cycl[ddCyclF];
+ if (comm->cycl_n[ddCyclF] > 1)
+ {
+ /* Subtract the maximum of the last n cycle counts
+ * to get rid of possible high counts due to other soures,
+ * for instance system activity, that would otherwise
+ * affect the dynamic load balancing.
+ */
+ load -= comm->cycl_max[ddCyclF];
+ }
+ }
+
+ return load;
+}
+
+static void set_slb_pme_dim_f(gmx_domdec_t *dd,int dim,real **dim_f)
+{
+ gmx_domdec_comm_t *comm;
+ int i;
+
+ comm = dd->comm;
+
+ snew(*dim_f,dd->nc[dim]+1);
+ (*dim_f)[0] = 0;
+ for(i=1; i<dd->nc[dim]; i++)
+ {
+ if (comm->slb_frac[dim])
+ {
+ (*dim_f)[i] = (*dim_f)[i-1] + comm->slb_frac[dim][i-1];
+ }
+ else
+ {
+ (*dim_f)[i] = (real)i/(real)dd->nc[dim];
+ }
+ }
+ (*dim_f)[dd->nc[dim]] = 1;
+}
+
+static void init_ddpme(gmx_domdec_t *dd,gmx_ddpme_t *ddpme,int dimind)
+{
+ int pmeindex,slab,nso,i;
+ ivec xyz;
+
+ if (dimind == 0 && dd->dim[0] == YY && dd->comm->npmenodes_x == 1)
+ {
+ ddpme->dim = YY;
+ }
+ else
+ {
+ ddpme->dim = dimind;
+ }
+ ddpme->dim_match = (ddpme->dim == dd->dim[dimind]);
+
+ ddpme->nslab = (ddpme->dim == 0 ?
+ dd->comm->npmenodes_x :
+ dd->comm->npmenodes_y);
+
+ if (ddpme->nslab <= 1)
+ {
+ return;
+ }
+
+ nso = dd->comm->npmenodes/ddpme->nslab;
+ /* Determine for each PME slab the PP location range for dimension dim */
+ snew(ddpme->pp_min,ddpme->nslab);
+ snew(ddpme->pp_max,ddpme->nslab);
+ for(slab=0; slab<ddpme->nslab; slab++) {
+ ddpme->pp_min[slab] = dd->nc[dd->dim[dimind]] - 1;
+ ddpme->pp_max[slab] = 0;
+ }
+ for(i=0; i<dd->nnodes; i++) {
+ ddindex2xyz(dd->nc,i,xyz);
+ /* For y only use our y/z slab.
+ * This assumes that the PME x grid size matches the DD grid size.
+ */
+ if (dimind == 0 || xyz[XX] == dd->ci[XX]) {
+ pmeindex = ddindex2pmeindex(dd,i);
+ if (dimind == 0) {
+ slab = pmeindex/nso;
+ } else {
+ slab = pmeindex % ddpme->nslab;
+ }
+ ddpme->pp_min[slab] = min(ddpme->pp_min[slab],xyz[dimind]);
+ ddpme->pp_max[slab] = max(ddpme->pp_max[slab],xyz[dimind]);
+ }
+ }
+
+ set_slb_pme_dim_f(dd,ddpme->dim,&ddpme->slb_dim_f);
+}
+
+int dd_pme_maxshift_x(gmx_domdec_t *dd)
+{
+ if (dd->comm->ddpme[0].dim == XX)
+ {
+ return dd->comm->ddpme[0].maxshift;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int dd_pme_maxshift_y(gmx_domdec_t *dd)
+{
+ if (dd->comm->ddpme[0].dim == YY)
+ {
+ return dd->comm->ddpme[0].maxshift;
+ }
+ else if (dd->comm->npmedecompdim >= 2 && dd->comm->ddpme[1].dim == YY)
+ {
+ return dd->comm->ddpme[1].maxshift;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static void set_pme_maxshift(gmx_domdec_t *dd,gmx_ddpme_t *ddpme,
+ gmx_bool bUniform,gmx_ddbox_t *ddbox,real *cell_f)
+{
+ gmx_domdec_comm_t *comm;
+ int nc,ns,s;
+ int *xmin,*xmax;
+ real range,pme_boundary;
+ int sh;
+
+ comm = dd->comm;
+ nc = dd->nc[ddpme->dim];
+ ns = ddpme->nslab;
+
+ if (!ddpme->dim_match)
+ {
+ /* PP decomposition is not along dim: the worst situation */
+ sh = ns/2;
+ }
+ else if (ns <= 3 || (bUniform && ns == nc))
+ {
+ /* The optimal situation */
+ sh = 1;
+ }
+ else
+ {
+ /* We need to check for all pme nodes which nodes they
+ * could possibly need to communicate with.
+ */
+ xmin = ddpme->pp_min;
+ xmax = ddpme->pp_max;
+ /* Allow for atoms to be maximally 2/3 times the cut-off
+ * out of their DD cell. This is a reasonable balance between
+ * between performance and support for most charge-group/cut-off
+ * combinations.
+ */
+ range = 2.0/3.0*comm->cutoff/ddbox->box_size[ddpme->dim];
+ /* Avoid extra communication when we are exactly at a boundary */
+ range *= 0.999;
+
+ sh = 1;
+ for(s=0; s<ns; s++)
+ {
+ /* PME slab s spreads atoms between box frac. s/ns and (s+1)/ns */
+ pme_boundary = (real)s/ns;
+ while (sh+1 < ns &&
+ ((s-(sh+1) >= 0 &&
+ cell_f[xmax[s-(sh+1) ]+1] + range > pme_boundary) ||
+ (s-(sh+1) < 0 &&
+ cell_f[xmax[s-(sh+1)+ns]+1] - 1 + range > pme_boundary)))
+ {
+ sh++;
+ }
+ pme_boundary = (real)(s+1)/ns;
+ while (sh+1 < ns &&
+ ((s+(sh+1) < ns &&
+ cell_f[xmin[s+(sh+1) ] ] - range < pme_boundary) ||
+ (s+(sh+1) >= ns &&
+ cell_f[xmin[s+(sh+1)-ns] ] + 1 - range < pme_boundary)))
+ {
+ sh++;
+ }
+ }
+ }
+
+ ddpme->maxshift = sh;
+
+ if (debug)
+ {
+ fprintf(debug,"PME slab communication range for dim %d is %d\n",
+ ddpme->dim,ddpme->maxshift);
+ }
+}
+
+static void check_box_size(gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
+{
+ int d,dim;
+
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ if (dim < ddbox->nboundeddim &&
+ ddbox->box_size[dim]*ddbox->skew_fac[dim] <
+ dd->nc[dim]*dd->comm->cellsize_limit*DD_CELL_MARGIN)
+ {
+ gmx_fatal(FARGS,"The %c-size of the box (%f) times the triclinic skew factor (%f) is smaller than the number of DD cells (%d) times the smallest allowed cell size (%f)\n",
+ dim2char(dim),ddbox->box_size[dim],ddbox->skew_fac[dim],
+ dd->nc[dim],dd->comm->cellsize_limit);
+ }
+ }
+}
+
+static void set_dd_cell_sizes_slb(gmx_domdec_t *dd,gmx_ddbox_t *ddbox,
+ gmx_bool bMaster,ivec npulse)
+{
+ gmx_domdec_comm_t *comm;
+ int d,j;
+ rvec cellsize_min;
+ real *cell_x,cell_dx,cellsize;
+
+ comm = dd->comm;
+
+ for(d=0; d<DIM; d++)
+ {
+ cellsize_min[d] = ddbox->box_size[d]*ddbox->skew_fac[d];
+ npulse[d] = 1;
+ if (dd->nc[d] == 1 || comm->slb_frac[d] == NULL)
+ {
+ /* Uniform grid */
+ cell_dx = ddbox->box_size[d]/dd->nc[d];
+ if (bMaster)
+ {
+ for(j=0; j<dd->nc[d]+1; j++)
+ {
+ dd->ma->cell_x[d][j] = ddbox->box0[d] + j*cell_dx;
+ }
+ }
+ else
+ {
+ comm->cell_x0[d] = ddbox->box0[d] + (dd->ci[d] )*cell_dx;
+ comm->cell_x1[d] = ddbox->box0[d] + (dd->ci[d]+1)*cell_dx;
+ }
+ cellsize = cell_dx*ddbox->skew_fac[d];
+ while (cellsize*npulse[d] < comm->cutoff && npulse[d] < dd->nc[d]-1)
+ {
+ npulse[d]++;
+ }
+ cellsize_min[d] = cellsize;
+ }
+ else
+ {
+ /* Statically load balanced grid */
+ /* Also when we are not doing a master distribution we determine
+ * all cell borders in a loop to obtain identical values
+ * to the master distribution case and to determine npulse.
+ */
+ if (bMaster)
+ {
+ cell_x = dd->ma->cell_x[d];
+ }
+ else
+ {
+ snew(cell_x,dd->nc[d]+1);
+ }
+ cell_x[0] = ddbox->box0[d];
+ for(j=0; j<dd->nc[d]; j++)
+ {
+ cell_dx = ddbox->box_size[d]*comm->slb_frac[d][j];
+ cell_x[j+1] = cell_x[j] + cell_dx;
+ cellsize = cell_dx*ddbox->skew_fac[d];
+ while (cellsize*npulse[d] < comm->cutoff &&
+ npulse[d] < dd->nc[d]-1)
+ {
+ npulse[d]++;
+ }
+ cellsize_min[d] = min(cellsize_min[d],cellsize);
+ }
+ if (!bMaster)
+ {
+ comm->cell_x0[d] = cell_x[dd->ci[d]];
+ comm->cell_x1[d] = cell_x[dd->ci[d]+1];
+ sfree(cell_x);
+ }
+ }
+ /* The following limitation is to avoid that a cell would receive
+ * some of its own home charge groups back over the periodic boundary.
+ * Double charge groups cause trouble with the global indices.
+ */
+ if (d < ddbox->npbcdim &&
+ dd->nc[d] > 1 && npulse[d] >= dd->nc[d])
+ {
+ gmx_fatal_collective(FARGS,NULL,dd,
+ "The box size in direction %c (%f) times the triclinic skew factor (%f) is too small for a cut-off of %f with %d domain decomposition cells, use 1 or more than %d %s or increase the box size in this direction",
+ dim2char(d),ddbox->box_size[d],ddbox->skew_fac[d],
+ comm->cutoff,
+ dd->nc[d],dd->nc[d],
+ dd->nnodes > dd->nc[d] ? "cells" : "processors");
+ }
+ }
+
+ if (!comm->bDynLoadBal)
+ {
+ copy_rvec(cellsize_min,comm->cellsize_min);
+ }
+
+ for(d=0; d<comm->npmedecompdim; d++)
+ {
+ set_pme_maxshift(dd,&comm->ddpme[d],
+ comm->slb_frac[dd->dim[d]]==NULL,ddbox,
+ comm->ddpme[d].slb_dim_f);
+ }
+}
+
+
+static void dd_cell_sizes_dlb_root_enforce_limits(gmx_domdec_t *dd,
+ int d,int dim,gmx_domdec_root_t *root,
+ gmx_ddbox_t *ddbox,
+ gmx_bool bUniform,gmx_large_int_t step, real cellsize_limit_f, int range[])
+{
+ gmx_domdec_comm_t *comm;
+ int ncd,i,j,nmin,nmin_old;
+ gmx_bool bLimLo,bLimHi;
+ real *cell_size;
+ real fac,halfway,cellsize_limit_f_i,region_size;
+ gmx_bool bPBC,bLastHi=FALSE;
+ int nrange[]={range[0],range[1]};
+
+ region_size= root->cell_f[range[1]]-root->cell_f[range[0]];
+
+ comm = dd->comm;
+
+ ncd = dd->nc[dim];
+
+ bPBC = (dim < ddbox->npbcdim);
+
+ cell_size = root->buf_ncd;
+
+ if (debug)
+ {
+ fprintf(debug,"enforce_limits: %d %d\n",range[0],range[1]);
+ }
+
+ /* First we need to check if the scaling does not make cells
+ * smaller than the smallest allowed size.
+ * We need to do this iteratively, since if a cell is too small,
+ * it needs to be enlarged, which makes all the other cells smaller,
+ * which could in turn make another cell smaller than allowed.
+ */
+ for(i=range[0]; i<range[1]; i++)
+ {
+ root->bCellMin[i] = FALSE;
+ }
+ nmin = 0;
+ do
+ {
+ nmin_old = nmin;
+ /* We need the total for normalization */
+ fac = 0;
+ for(i=range[0]; i<range[1]; i++)
+ {
+ if (root->bCellMin[i] == FALSE)
+ {
+ fac += cell_size[i];
+ }
+ }
+ fac = ( region_size - nmin*cellsize_limit_f)/fac; /* substracting cells already set to cellsize_limit_f */
+ /* Determine the cell boundaries */
+ for(i=range[0]; i<range[1]; i++)
+ {
+ if (root->bCellMin[i] == FALSE)
+ {
+ cell_size[i] *= fac;
+ if (!bPBC && (i == 0 || i == dd->nc[dim] -1))
+ {
+ cellsize_limit_f_i = 0;
+ }
+ else
+ {
+ cellsize_limit_f_i = cellsize_limit_f;
+ }
+ if (cell_size[i] < cellsize_limit_f_i)
+ {
+ root->bCellMin[i] = TRUE;
+ cell_size[i] = cellsize_limit_f_i;
+ nmin++;
+ }
+ }
+ root->cell_f[i+1] = root->cell_f[i] + cell_size[i];
+ }
+ }
+ while (nmin > nmin_old);
+
+ i=range[1]-1;
+ cell_size[i] = root->cell_f[i+1] - root->cell_f[i];
+ /* For this check we should not use DD_CELL_MARGIN,
+ * but a slightly smaller factor,
+ * since rounding could get use below the limit.
+ */
+ if (bPBC && cell_size[i] < cellsize_limit_f*DD_CELL_MARGIN2/DD_CELL_MARGIN)
+ {
+ char buf[22];
+ gmx_fatal(FARGS,"Step %s: the dynamic load balancing could not balance dimension %c: box size %f, triclinic skew factor %f, #cells %d, minimum cell size %f\n",
+ gmx_step_str(step,buf),
+ dim2char(dim),ddbox->box_size[dim],ddbox->skew_fac[dim],
+ ncd,comm->cellsize_min[dim]);
+ }
+
+ root->bLimited = (nmin > 0) || (range[0]>0) || (range[1]<ncd);
+
+ if (!bUniform)
+ {
+ /* Check if the boundary did not displace more than halfway
+ * each of the cells it bounds, as this could cause problems,
+ * especially when the differences between cell sizes are large.
+ * If changes are applied, they will not make cells smaller
+ * than the cut-off, as we check all the boundaries which
+ * might be affected by a change and if the old state was ok,
+ * the cells will at most be shrunk back to their old size.
+ */
+ for(i=range[0]+1; i<range[1]; i++)
+ {
+ halfway = 0.5*(root->old_cell_f[i] + root->old_cell_f[i-1]);
+ if (root->cell_f[i] < halfway)
+ {
+ root->cell_f[i] = halfway;
+ /* Check if the change also causes shifts of the next boundaries */
+ for(j=i+1; j<range[1]; j++)
+ {
+ if (root->cell_f[j] < root->cell_f[j-1] + cellsize_limit_f)
+ root->cell_f[j] = root->cell_f[j-1] + cellsize_limit_f;
+ }
+ }
+ halfway = 0.5*(root->old_cell_f[i] + root->old_cell_f[i+1]);
+ if (root->cell_f[i] > halfway)
+ {
+ root->cell_f[i] = halfway;
+ /* Check if the change also causes shifts of the next boundaries */
+ for(j=i-1; j>=range[0]+1; j--)
+ {
+ if (root->cell_f[j] > root->cell_f[j+1] - cellsize_limit_f)
+ root->cell_f[j] = root->cell_f[j+1] - cellsize_limit_f;
+ }
+ }
+ }
+ }
+
+ /* nrange is defined as [lower, upper) range for new call to enforce_limits */
+ /* find highest violation of LimLo (a) and the following violation of LimHi (thus the lowest following) (b)
+ * then call enforce_limits for (oldb,a), (a,b). In the next step: (b,nexta). oldb and nexta can be the boundaries.
+ * for a and b nrange is used */
+ if (d > 0)
+ {
+ /* Take care of the staggering of the cell boundaries */
+ if (bUniform)
+ {
+ for(i=range[0]; i<range[1]; i++)
+ {
+ root->cell_f_max0[i] = root->cell_f[i];
+ root->cell_f_min1[i] = root->cell_f[i+1];
+ }
+ }
+ else
+ {
+ for(i=range[0]+1; i<range[1]; i++)
+ {
+ bLimLo = (root->cell_f[i] < root->bound_min[i]);
+ bLimHi = (root->cell_f[i] > root->bound_max[i]);
+ if (bLimLo && bLimHi)
+ {
+ /* Both limits violated, try the best we can */
+ /* For this case we split the original range (range) in two parts and care about the other limitiations in the next iteration. */
+ root->cell_f[i] = 0.5*(root->bound_min[i] + root->bound_max[i]);
+ nrange[0]=range[0];
+ nrange[1]=i;
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+
+ nrange[0]=i;
+ nrange[1]=range[1];
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+
+ return;
+ }
+ else if (bLimLo)
+ {
+ /* root->cell_f[i] = root->bound_min[i]; */
+ nrange[1]=i; /* only store violation location. There could be a LimLo violation following with an higher index */
+ bLastHi=FALSE;
+ }
+ else if (bLimHi && !bLastHi)
+ {
+ bLastHi=TRUE;
+ if (nrange[1] < range[1]) /* found a LimLo before */
+ {
+ root->cell_f[nrange[1]] = root->bound_min[nrange[1]];
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+ nrange[0]=nrange[1];
+ }
+ root->cell_f[i] = root->bound_max[i];
+ nrange[1]=i;
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+ nrange[0]=i;
+ nrange[1]=range[1];
+ }
+ }
+ if (nrange[1] < range[1]) /* found last a LimLo */
+ {
+ root->cell_f[nrange[1]] = root->bound_min[nrange[1]];
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+ nrange[0]=nrange[1];
+ nrange[1]=range[1];
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+ }
+ else if (nrange[0] > range[0]) /* found at least one LimHi */
+ {
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
+ }
+ }
+ }
+}
+
+
+static void set_dd_cell_sizes_dlb_root(gmx_domdec_t *dd,
+ int d,int dim,gmx_domdec_root_t *root,
+ gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
+ gmx_bool bUniform,gmx_large_int_t step)
+{
+ gmx_domdec_comm_t *comm;
+ int ncd,d1,i,j,pos;
+ real *cell_size;
+ real load_aver,load_i,imbalance,change,change_max,sc;
+ real cellsize_limit_f,dist_min_f,dist_min_f_hard,space;
+ real change_limit = 0.1;
+ real relax = 0.5;
+ gmx_bool bPBC;
+ int range[] = { 0, 0 };
+
+ comm = dd->comm;
+
+ ncd = dd->nc[dim];
+
+ bPBC = (dim < ddbox->npbcdim);
+
+ cell_size = root->buf_ncd;
+
+ /* Store the original boundaries */
+ for(i=0; i<ncd+1; i++)
+ {
+ root->old_cell_f[i] = root->cell_f[i];
+ }
+ if (bUniform) {
+ for(i=0; i<ncd; i++)
+ {
+ cell_size[i] = 1.0/ncd;
+ }
+ }
+ else if (dd_load_count(comm))
+ {
+ load_aver = comm->load[d].sum_m/ncd;
+ change_max = 0;
+ for(i=0; i<ncd; i++)
+ {
+ /* Determine the relative imbalance of cell i */
+ load_i = comm->load[d].load[i*comm->load[d].nload+2];
+ imbalance = (load_i - load_aver)/(load_aver>0 ? load_aver : 1);
+ /* Determine the change of the cell size using underrelaxation */
+ change = -relax*imbalance;
+ change_max = max(change_max,max(change,-change));
+ }
+ /* Limit the amount of scaling.
+ * We need to use the same rescaling for all cells in one row,
+ * otherwise the load balancing might not converge.
+ */
+ sc = relax;
+ if (change_max > change_limit)
+ {
+ sc *= change_limit/change_max;
+ }
+ for(i=0; i<ncd; i++)
+ {
+ /* Determine the relative imbalance of cell i */
+ load_i = comm->load[d].load[i*comm->load[d].nload+2];
+ imbalance = (load_i - load_aver)/(load_aver>0 ? load_aver : 1);
+ /* Determine the change of the cell size using underrelaxation */
+ change = -sc*imbalance;
+ cell_size[i] = (root->cell_f[i+1]-root->cell_f[i])*(1 + change);
+ }
+ }
+
+ cellsize_limit_f = comm->cellsize_min[dim]/ddbox->box_size[dim];
+ cellsize_limit_f *= DD_CELL_MARGIN;
+ dist_min_f_hard = grid_jump_limit(comm,d)/ddbox->box_size[dim];
+ dist_min_f = dist_min_f_hard * DD_CELL_MARGIN;
+ if (ddbox->tric_dir[dim])
+ {
+ cellsize_limit_f /= ddbox->skew_fac[dim];
+ dist_min_f /= ddbox->skew_fac[dim];
+ }
+ if (bDynamicBox && d > 0)
+ {
+ dist_min_f *= DD_PRES_SCALE_MARGIN;
+ }
+ if (d > 0 && !bUniform)
+ {
+ /* Make sure that the grid is not shifted too much */
+ for(i=1; i<ncd; i++) {
+ if (root->cell_f_min1[i] - root->cell_f_max0[i-1] < 2 * dist_min_f_hard)
+ {
+ gmx_incons("Inconsistent DD boundary staggering limits!");
+ }
+ root->bound_min[i] = root->cell_f_max0[i-1] + dist_min_f;
+ space = root->cell_f[i] - (root->cell_f_max0[i-1] + dist_min_f);
+ if (space > 0) {
+ root->bound_min[i] += 0.5*space;
+ }
+ root->bound_max[i] = root->cell_f_min1[i] - dist_min_f;
+ space = root->cell_f[i] - (root->cell_f_min1[i] - dist_min_f);
+ if (space < 0) {
+ root->bound_max[i] += 0.5*space;
+ }
+ if (debug)
+ {
+ fprintf(debug,
+ "dim %d boundary %d %.3f < %.3f < %.3f < %.3f < %.3f\n",
+ d,i,
+ root->cell_f_max0[i-1] + dist_min_f,
+ root->bound_min[i],root->cell_f[i],root->bound_max[i],
+ root->cell_f_min1[i] - dist_min_f);
+ }
+ }
+ }
+ range[1]=ncd;
+ root->cell_f[0] = 0;
+ root->cell_f[ncd] = 1;
+ dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, range);
+
+
+ /* After the checks above, the cells should obey the cut-off
+ * restrictions, but it does not hurt to check.
+ */
+ for(i=0; i<ncd; i++)
+ {
+ if (debug)
+ {
+ fprintf(debug,"Relative bounds dim %d cell %d: %f %f\n",
+ dim,i,root->cell_f[i],root->cell_f[i+1]);
+ }
+
+ if ((bPBC || (i != 0 && i != dd->nc[dim]-1)) &&
+ root->cell_f[i+1] - root->cell_f[i] <
+ cellsize_limit_f/DD_CELL_MARGIN)
+ {
+ char buf[22];
+ fprintf(stderr,
+ "\nWARNING step %s: direction %c, cell %d too small: %f\n",
+ gmx_step_str(step,buf),dim2char(dim),i,
+ (root->cell_f[i+1] - root->cell_f[i])
+ *ddbox->box_size[dim]*ddbox->skew_fac[dim]);
+ }
+ }
+
+ pos = ncd + 1;
+ /* Store the cell boundaries of the lower dimensions at the end */
+ for(d1=0; d1<d; d1++)
+ {
+ root->cell_f[pos++] = comm->cell_f0[d1];
+ root->cell_f[pos++] = comm->cell_f1[d1];
+ }
+
+ if (d < comm->npmedecompdim)
+ {
+ /* The master determines the maximum shift for
+ * the coordinate communication between separate PME nodes.
+ */
+ set_pme_maxshift(dd,&comm->ddpme[d],bUniform,ddbox,root->cell_f);
+ }
+ root->cell_f[pos++] = comm->ddpme[0].maxshift;
+ if (d >= 1)
+ {
+ root->cell_f[pos++] = comm->ddpme[1].maxshift;
+ }
+}
+
+static void relative_to_absolute_cell_bounds(gmx_domdec_t *dd,
+ gmx_ddbox_t *ddbox,int dimind)
+{
+ gmx_domdec_comm_t *comm;
+ int dim;
+
+ comm = dd->comm;
+
+ /* Set the cell dimensions */
+ dim = dd->dim[dimind];
+ comm->cell_x0[dim] = comm->cell_f0[dimind]*ddbox->box_size[dim];
+ comm->cell_x1[dim] = comm->cell_f1[dimind]*ddbox->box_size[dim];
+ if (dim >= ddbox->nboundeddim)
+ {
+ comm->cell_x0[dim] += ddbox->box0[dim];
+ comm->cell_x1[dim] += ddbox->box0[dim];
+ }
+}
+
+static void distribute_dd_cell_sizes_dlb(gmx_domdec_t *dd,
+ int d,int dim,real *cell_f_row,
+ gmx_ddbox_t *ddbox)
+{
+ gmx_domdec_comm_t *comm;
+ int d1,dim1,pos;
+
+ comm = dd->comm;
+
+#ifdef GMX_MPI
+ /* Each node would only need to know two fractions,
+ * but it is probably cheaper to broadcast the whole array.
+ */
+ MPI_Bcast(cell_f_row,DD_CELL_F_SIZE(dd,d)*sizeof(real),MPI_BYTE,
+ 0,comm->mpi_comm_load[d]);
+#endif
+ /* Copy the fractions for this dimension from the buffer */
+ comm->cell_f0[d] = cell_f_row[dd->ci[dim] ];
+ comm->cell_f1[d] = cell_f_row[dd->ci[dim]+1];
+ /* The whole array was communicated, so set the buffer position */
+ pos = dd->nc[dim] + 1;
+ for(d1=0; d1<=d; d1++)
+ {
+ if (d1 < d)
+ {
+ /* Copy the cell fractions of the lower dimensions */
+ comm->cell_f0[d1] = cell_f_row[pos++];
+ comm->cell_f1[d1] = cell_f_row[pos++];
+ }
+ relative_to_absolute_cell_bounds(dd,ddbox,d1);
+ }
+ /* Convert the communicated shift from float to int */
+ comm->ddpme[0].maxshift = (int)(cell_f_row[pos++] + 0.5);
+ if (d >= 1)
+ {
+ comm->ddpme[1].maxshift = (int)(cell_f_row[pos++] + 0.5);
+ }
+}
+
+static void set_dd_cell_sizes_dlb_change(gmx_domdec_t *dd,
+ gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
+ gmx_bool bUniform,gmx_large_int_t step)
+{
+ gmx_domdec_comm_t *comm;
+ int d,dim,d1;
+ gmx_bool bRowMember,bRowRoot;
+ real *cell_f_row;
+
+ comm = dd->comm;
+
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ bRowMember = TRUE;
+ bRowRoot = TRUE;
+ for(d1=d; d1<dd->ndim; d1++)
+ {
+ if (dd->ci[dd->dim[d1]] > 0)
+ {
+ if (d1 > d)
+ {
+ bRowMember = FALSE;
+ }
+ bRowRoot = FALSE;
+ }
+ }
+ if (bRowMember)
+ {
+ if (bRowRoot)
+ {
+ set_dd_cell_sizes_dlb_root(dd,d,dim,comm->root[d],
+ ddbox,bDynamicBox,bUniform,step);
+ cell_f_row = comm->root[d]->cell_f;
+ }
+ else
+ {
+ cell_f_row = comm->cell_f_row;
+ }
+ distribute_dd_cell_sizes_dlb(dd,d,dim,cell_f_row,ddbox);
+ }
+ }
+}
+
+static void set_dd_cell_sizes_dlb_nochange(gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
+{
+ int d;
+
+ /* This function assumes the box is static and should therefore
+ * not be called when the box has changed since the last
+ * call to dd_partition_system.
+ */
+ for(d=0; d<dd->ndim; d++)
+ {
+ relative_to_absolute_cell_bounds(dd,ddbox,d);
+ }
+}
+
+
+
+static void set_dd_cell_sizes_dlb(gmx_domdec_t *dd,
+ gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
+ gmx_bool bUniform,gmx_bool bDoDLB,gmx_large_int_t step,
+ gmx_wallcycle_t wcycle)
+{
+ gmx_domdec_comm_t *comm;
+ int dim;
+
+ comm = dd->comm;
+
+ if (bDoDLB)
+ {
+ wallcycle_start(wcycle,ewcDDCOMMBOUND);
+ set_dd_cell_sizes_dlb_change(dd,ddbox,bDynamicBox,bUniform,step);
+ wallcycle_stop(wcycle,ewcDDCOMMBOUND);
+ }
+ else if (bDynamicBox)
+ {
+ set_dd_cell_sizes_dlb_nochange(dd,ddbox);
+ }
+
+ /* Set the dimensions for which no DD is used */
+ for(dim=0; dim<DIM; dim++) {
+ if (dd->nc[dim] == 1) {
+ comm->cell_x0[dim] = 0;
+ comm->cell_x1[dim] = ddbox->box_size[dim];
+ if (dim >= ddbox->nboundeddim)
+ {
+ comm->cell_x0[dim] += ddbox->box0[dim];
+ comm->cell_x1[dim] += ddbox->box0[dim];
+ }
+ }
+ }
+}
+
+static void realloc_comm_ind(gmx_domdec_t *dd,ivec npulse)
+{
+ int d,np,i;
+ gmx_domdec_comm_dim_t *cd;
+
+ for(d=0; d<dd->ndim; d++)
+ {
+ cd = &dd->comm->cd[d];
+ np = npulse[dd->dim[d]];
+ if (np > cd->np_nalloc)
+ {
+ if (debug)
+ {
+ fprintf(debug,"(Re)allocing cd for %c to %d pulses\n",
+ dim2char(dd->dim[d]),np);
+ }
+ if (DDMASTER(dd) && cd->np_nalloc > 0)
+ {
+ fprintf(stderr,"\nIncreasing the number of cell to communicate in dimension %c to %d for the first time\n",dim2char(dd->dim[d]),np);
+ }
+ srenew(cd->ind,np);
+ for(i=cd->np_nalloc; i<np; i++)
+ {
+ cd->ind[i].index = NULL;
+ cd->ind[i].nalloc = 0;
+ }
+ cd->np_nalloc = np;
+ }
+ cd->np = np;
+ }
+}
+
+
+static void set_dd_cell_sizes(gmx_domdec_t *dd,
+ gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
+ gmx_bool bUniform,gmx_bool bDoDLB,gmx_large_int_t step,
+ gmx_wallcycle_t wcycle)
+{
+ gmx_domdec_comm_t *comm;
+ int d;
+ ivec npulse;
+
+ comm = dd->comm;
+
+ /* Copy the old cell boundaries for the cg displacement check */
+ copy_rvec(comm->cell_x0,comm->old_cell_x0);
+ copy_rvec(comm->cell_x1,comm->old_cell_x1);
+
+ if (comm->bDynLoadBal)
+ {
+ if (DDMASTER(dd))
+ {
+ check_box_size(dd,ddbox);
+ }
+ set_dd_cell_sizes_dlb(dd,ddbox,bDynamicBox,bUniform,bDoDLB,step,wcycle);
+ }
+ else
+ {
+ set_dd_cell_sizes_slb(dd,ddbox,FALSE,npulse);
+ realloc_comm_ind(dd,npulse);
+ }
+
+ if (debug)
+ {
+ for(d=0; d<DIM; d++)
+ {
+ fprintf(debug,"cell_x[%d] %f - %f skew_fac %f\n",
+ d,comm->cell_x0[d],comm->cell_x1[d],ddbox->skew_fac[d]);
+ }
+ }
+}
+
+static void comm_dd_ns_cell_sizes(gmx_domdec_t *dd,
+ gmx_ddbox_t *ddbox,
+ rvec cell_ns_x0,rvec cell_ns_x1,
+ gmx_large_int_t step)
+{
+ gmx_domdec_comm_t *comm;
+ int dim_ind,dim;
+
+ comm = dd->comm;
+
+ for(dim_ind=0; dim_ind<dd->ndim; dim_ind++)
+ {
+ dim = dd->dim[dim_ind];
+
+ /* Without PBC we don't have restrictions on the outer cells */
+ if (!(dim >= ddbox->npbcdim &&
+ (dd->ci[dim] == 0 || dd->ci[dim] == dd->nc[dim] - 1)) &&
+ comm->bDynLoadBal &&
+ (comm->cell_x1[dim] - comm->cell_x0[dim])*ddbox->skew_fac[dim] <
+ comm->cellsize_min[dim])
+ {
+ char buf[22];
+ gmx_fatal(FARGS,"Step %s: The %c-size (%f) times the triclinic skew factor (%f) is smaller than the smallest allowed cell size (%f) for domain decomposition grid cell %d %d %d",
+ gmx_step_str(step,buf),dim2char(dim),
+ comm->cell_x1[dim] - comm->cell_x0[dim],
+ ddbox->skew_fac[dim],
+ dd->comm->cellsize_min[dim],
+ dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
+ }
+ }
+
+ if ((dd->bGridJump && dd->ndim > 1) || ddbox->nboundeddim < DIM)
+ {
+ /* Communicate the boundaries and update cell_ns_x0/1 */
+ dd_move_cellx(dd,ddbox,cell_ns_x0,cell_ns_x1);
+ if (dd->bGridJump && dd->ndim > 1)
+ {
+ check_grid_jump(step,dd,ddbox);
+ }
+ }
+}
+
+static void make_tric_corr_matrix(int npbcdim,matrix box,matrix tcm)
+{
+ if (YY < npbcdim)
+ {
+ tcm[YY][XX] = -box[YY][XX]/box[YY][YY];
+ }
+ else
+ {
+ tcm[YY][XX] = 0;
+ }
+ if (ZZ < npbcdim)
+ {
+ tcm[ZZ][XX] = -(box[ZZ][YY]*tcm[YY][XX] + box[ZZ][XX])/box[ZZ][ZZ];
+ tcm[ZZ][YY] = -box[ZZ][YY]/box[ZZ][ZZ];
+ }
+ else
+ {
+ tcm[ZZ][XX] = 0;
+ tcm[ZZ][YY] = 0;
+ }
+}
+
+static void check_screw_box(matrix box)
+{
+ /* Mathematical limitation */
+ if (box[YY][XX] != 0 || box[ZZ][XX] != 0)
+ {
+ gmx_fatal(FARGS,"With screw pbc the unit cell can not have non-zero off-diagonal x-components");
+ }
+
+ /* Limitation due to the asymmetry of the eighth shell method */
+ if (box[ZZ][YY] != 0)
+ {
+ gmx_fatal(FARGS,"pbc=screw with non-zero box_zy is not supported");
+ }
+}
+
+static void distribute_cg(FILE *fplog,gmx_large_int_t step,
+ matrix box,ivec tric_dir,t_block *cgs,rvec pos[],
+ gmx_domdec_t *dd)
+{
+ gmx_domdec_master_t *ma;
+ int **tmp_ind=NULL,*tmp_nalloc=NULL;
+ int i,icg,j,k,k0,k1,d,npbcdim;
+ matrix tcm;
+ rvec box_size,cg_cm;
+ ivec ind;
+ real nrcg,inv_ncg,pos_d;
+ atom_id *cgindex;
+ gmx_bool bUnbounded,bScrew;
+
+ ma = dd->ma;
+
+ if (tmp_ind == NULL)
+ {
+ snew(tmp_nalloc,dd->nnodes);
+ snew(tmp_ind,dd->nnodes);
+ for(i=0; i<dd->nnodes; i++)
+ {
+ tmp_nalloc[i] = over_alloc_large(cgs->nr/dd->nnodes+1);
+ snew(tmp_ind[i],tmp_nalloc[i]);
+ }
+ }
+
+ /* Clear the count */
+ for(i=0; i<dd->nnodes; i++)
+ {
+ ma->ncg[i] = 0;
+ ma->nat[i] = 0;
+ }
+
+ make_tric_corr_matrix(dd->npbcdim,box,tcm);
+
+ cgindex = cgs->index;
+
+ /* Compute the center of geometry for all charge groups */
+ for(icg=0; icg<cgs->nr; icg++)
+ {
+ k0 = cgindex[icg];
+ k1 = cgindex[icg+1];
+ nrcg = k1 - k0;
+ if (nrcg == 1)
+ {
+ copy_rvec(pos[k0],cg_cm);
+ }
+ else
+ {
+ inv_ncg = 1.0/nrcg;
+
+ clear_rvec(cg_cm);
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_inc(cg_cm,pos[k]);
+ }
+ for(d=0; (d<DIM); d++)
+ {
+ cg_cm[d] *= inv_ncg;
+ }
+ }
+ /* Put the charge group in the box and determine the cell index */
+ for(d=DIM-1; d>=0; d--) {
+ pos_d = cg_cm[d];
+ if (d < dd->npbcdim)
+ {
+ bScrew = (dd->bScrewPBC && d == XX);
+ if (tric_dir[d] && dd->nc[d] > 1)
+ {
+ /* Use triclinic coordintates for this dimension */
+ for(j=d+1; j<DIM; j++)
+ {
+ pos_d += cg_cm[j]*tcm[j][d];
+ }
+ }
+ while(pos_d >= box[d][d])
+ {
+ pos_d -= box[d][d];
+ rvec_dec(cg_cm,box[d]);
+ if (bScrew)
+ {
+ cg_cm[YY] = box[YY][YY] - cg_cm[YY];
+ cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
+ }
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_dec(pos[k],box[d]);
+ if (bScrew)
+ {
+ pos[k][YY] = box[YY][YY] - pos[k][YY];
+ pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
+ }
+ }
+ }
+ while(pos_d < 0)
+ {
+ pos_d += box[d][d];
+ rvec_inc(cg_cm,box[d]);
+ if (bScrew)
+ {
+ cg_cm[YY] = box[YY][YY] - cg_cm[YY];
+ cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
+ }
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_inc(pos[k],box[d]);
+ if (bScrew) {
+ pos[k][YY] = box[YY][YY] - pos[k][YY];
+ pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
+ }
+ }
+ }
+ }
+ /* This could be done more efficiently */
+ ind[d] = 0;
+ while(ind[d]+1 < dd->nc[d] && pos_d >= ma->cell_x[d][ind[d]+1])
+ {
+ ind[d]++;
+ }
+ }
+ i = dd_index(dd->nc,ind);
+ if (ma->ncg[i] == tmp_nalloc[i])
+ {
+ tmp_nalloc[i] = over_alloc_large(ma->ncg[i]+1);
+ srenew(tmp_ind[i],tmp_nalloc[i]);
+ }
+ tmp_ind[i][ma->ncg[i]] = icg;
+ ma->ncg[i]++;
+ ma->nat[i] += cgindex[icg+1] - cgindex[icg];
+ }
+
+ k1 = 0;
+ for(i=0; i<dd->nnodes; i++)
+ {
+ ma->index[i] = k1;
+ for(k=0; k<ma->ncg[i]; k++)
+ {
+ ma->cg[k1++] = tmp_ind[i][k];
+ }
+ }
+ ma->index[dd->nnodes] = k1;
+
+ for(i=0; i<dd->nnodes; i++)
+ {
+ sfree(tmp_ind[i]);
+ }
+ sfree(tmp_ind);
+ sfree(tmp_nalloc);
+
+ if (fplog)
+ {
+ char buf[22];
+ fprintf(fplog,"Charge group distribution at step %s:",
+ gmx_step_str(step,buf));
+ for(i=0; i<dd->nnodes; i++)
+ {
+ fprintf(fplog," %d",ma->ncg[i]);
+ }
+ fprintf(fplog,"\n");
+ }
+}
+
+static void get_cg_distribution(FILE *fplog,gmx_large_int_t step,gmx_domdec_t *dd,
+ t_block *cgs,matrix box,gmx_ddbox_t *ddbox,
+ rvec pos[])
+{
+ gmx_domdec_master_t *ma=NULL;
+ ivec npulse;
+ int i,cg_gl;
+ int *ibuf,buf2[2] = { 0, 0 };
+
+ if (DDMASTER(dd))
+ {
+ ma = dd->ma;
+
+ if (dd->bScrewPBC)
+ {
+ check_screw_box(box);
+ }
+
+ set_dd_cell_sizes_slb(dd,ddbox,TRUE,npulse);
+
+ distribute_cg(fplog,step,box,ddbox->tric_dir,cgs,pos,dd);
+ for(i=0; i<dd->nnodes; i++)
+ {
+ ma->ibuf[2*i] = ma->ncg[i];
+ ma->ibuf[2*i+1] = ma->nat[i];
+ }
+ ibuf = ma->ibuf;
+ }
+ else
+ {
+ ibuf = NULL;
+ }
+ dd_scatter(dd,2*sizeof(int),ibuf,buf2);
+
+ dd->ncg_home = buf2[0];
+ dd->nat_home = buf2[1];
+ dd->ncg_tot = dd->ncg_home;
+ dd->nat_tot = dd->nat_home;
+ if (dd->ncg_home > dd->cg_nalloc || dd->cg_nalloc == 0)
+ {
+ dd->cg_nalloc = over_alloc_dd(dd->ncg_home);
+ srenew(dd->index_gl,dd->cg_nalloc);
+ srenew(dd->cgindex,dd->cg_nalloc+1);
+ }
+ if (DDMASTER(dd))
+ {
+ for(i=0; i<dd->nnodes; i++)
+ {
+ ma->ibuf[i] = ma->ncg[i]*sizeof(int);
+ ma->ibuf[dd->nnodes+i] = ma->index[i]*sizeof(int);
+ }
+ }
+
+ dd_scatterv(dd,
+ DDMASTER(dd) ? ma->ibuf : NULL,
+ DDMASTER(dd) ? ma->ibuf+dd->nnodes : NULL,
+ DDMASTER(dd) ? ma->cg : NULL,
+ dd->ncg_home*sizeof(int),dd->index_gl);
+
+ /* Determine the home charge group sizes */
+ dd->cgindex[0] = 0;
+ for(i=0; i<dd->ncg_home; i++)
+ {
+ cg_gl = dd->index_gl[i];
+ dd->cgindex[i+1] =
+ dd->cgindex[i] + cgs->index[cg_gl+1] - cgs->index[cg_gl];
+ }
+
+ if (debug)
+ {
+ fprintf(debug,"Home charge groups:\n");
+ for(i=0; i<dd->ncg_home; i++)
+ {
+ fprintf(debug," %d",dd->index_gl[i]);
+ if (i % 10 == 9)
+ fprintf(debug,"\n");
+ }
+ fprintf(debug,"\n");
+ }
+}
+
+static int compact_and_copy_vec_at(int ncg,int *move,
+ int *cgindex,
+ int nvec,int vec,
+ rvec *src,gmx_domdec_comm_t *comm,
+ gmx_bool bCompact)
+{
+ int m,icg,i,i0,i1,nrcg;
+ int home_pos;
+ int pos_vec[DIM*2];
+
+ home_pos = 0;
+
+ for(m=0; m<DIM*2; m++)
+ {
+ pos_vec[m] = 0;
+ }
+
+ i0 = 0;
+ for(icg=0; icg<ncg; icg++)
+ {
+ i1 = cgindex[icg+1];
+ m = move[icg];
+ if (m == -1)
+ {
+ if (bCompact)
+ {
+ /* Compact the home array in place */
+ for(i=i0; i<i1; i++)
+ {
+ copy_rvec(src[i],src[home_pos++]);
+ }
+ }
+ }
+ else
+ {
+ /* Copy to the communication buffer */
+ nrcg = i1 - i0;
+ pos_vec[m] += 1 + vec*nrcg;
+ for(i=i0; i<i1; i++)
+ {
+ copy_rvec(src[i],comm->cgcm_state[m][pos_vec[m]++]);
+ }
+ pos_vec[m] += (nvec - vec - 1)*nrcg;
+ }
+ if (!bCompact)
+ {
+ home_pos += i1 - i0;
+ }
+ i0 = i1;
+ }
+
+ return home_pos;
+}
+
+static int compact_and_copy_vec_cg(int ncg,int *move,
+ int *cgindex,
+ int nvec,rvec *src,gmx_domdec_comm_t *comm,
+ gmx_bool bCompact)
+{
+ int m,icg,i0,i1,nrcg;
+ int home_pos;
+ int pos_vec[DIM*2];
+
+ home_pos = 0;
+
+ for(m=0; m<DIM*2; m++)
+ {
+ pos_vec[m] = 0;
+ }
+
+ i0 = 0;
+ for(icg=0; icg<ncg; icg++)
+ {
+ i1 = cgindex[icg+1];
+ m = move[icg];
+ if (m == -1)
+ {
+ if (bCompact)
+ {
+ /* Compact the home array in place */
+ copy_rvec(src[icg],src[home_pos++]);
+ }
+ }
+ else
+ {
+ nrcg = i1 - i0;
+ /* Copy to the communication buffer */
+ copy_rvec(src[icg],comm->cgcm_state[m][pos_vec[m]]);
+ pos_vec[m] += 1 + nrcg*nvec;
+ }
+ i0 = i1;
+ }
+ if (!bCompact)
+ {
+ home_pos = ncg;
+ }
+
+ return home_pos;
+}
+
+static int compact_ind(int ncg,int *move,
+ int *index_gl,int *cgindex,
+ int *gatindex,
+ gmx_ga2la_t ga2la,char *bLocalCG,
+ int *cginfo)
+{
+ int cg,nat,a0,a1,a,a_gl;
+ int home_pos;
+
+ home_pos = 0;
+ nat = 0;
+ for(cg=0; cg<ncg; cg++)
+ {
+ a0 = cgindex[cg];
+ a1 = cgindex[cg+1];
+ if (move[cg] == -1)
+ {
+ /* Compact the home arrays in place.
+ * Anything that can be done here avoids access to global arrays.
+ */
+ cgindex[home_pos] = nat;
+ for(a=a0; a<a1; a++)
+ {
+ a_gl = gatindex[a];
+ gatindex[nat] = a_gl;
+ /* The cell number stays 0, so we don't need to set it */
+ ga2la_change_la(ga2la,a_gl,nat);
+ nat++;
+ }
+ index_gl[home_pos] = index_gl[cg];
+ cginfo[home_pos] = cginfo[cg];
+ /* The charge group remains local, so bLocalCG does not change */
+ home_pos++;
+ }
+ else
+ {
+ /* Clear the global indices */
+ for(a=a0; a<a1; a++)
+ {
+ ga2la_del(ga2la,gatindex[a]);
+ }
+ if (bLocalCG)
+ {
+ bLocalCG[index_gl[cg]] = FALSE;
+ }
+ }
+ }
+ cgindex[home_pos] = nat;
+
+ return home_pos;
+}
+
+static void clear_and_mark_ind(int ncg,int *move,
+ int *index_gl,int *cgindex,int *gatindex,
+ gmx_ga2la_t ga2la,char *bLocalCG,
+ int *cell_index)
+{
+ int cg,a0,a1,a;
+
+ for(cg=0; cg<ncg; cg++)
+ {
+ if (move[cg] >= 0)
+ {
+ a0 = cgindex[cg];
+ a1 = cgindex[cg+1];
+ /* Clear the global indices */
+ for(a=a0; a<a1; a++)
+ {
+ ga2la_del(ga2la,gatindex[a]);
+ }
+ if (bLocalCG)
+ {
+ bLocalCG[index_gl[cg]] = FALSE;
+ }
+ /* Signal that this cg has moved using the ns cell index.
+ * Here we set it to -1.
+ * fill_grid will change it from -1 to 4*grid->ncells.
+ */
+ cell_index[cg] = -1;
+ }
+ }
+}
+
+static void print_cg_move(FILE *fplog,
+ gmx_domdec_t *dd,
+ gmx_large_int_t step,int cg,int dim,int dir,
+ gmx_bool bHaveLimitdAndCMOld,real limitd,
+ rvec cm_old,rvec cm_new,real pos_d)
+{
+ gmx_domdec_comm_t *comm;
+ char buf[22];
+
+ comm = dd->comm;
+
+ fprintf(fplog,"\nStep %s:\n",gmx_step_str(step,buf));
+ if (bHaveLimitdAndCMOld)
+ {
+ fprintf(fplog,"The charge group starting at atom %d moved than the distance allowed by the domain decomposition (%f) in direction %c\n",
+ ddglatnr(dd,dd->cgindex[cg]),limitd,dim2char(dim));
+ }
+ else
+ {
+ fprintf(fplog,"The charge group starting at atom %d moved than the distance allowed by the domain decomposition in direction %c\n",
+ ddglatnr(dd,dd->cgindex[cg]),dim2char(dim));
+ }
+ fprintf(fplog,"distance out of cell %f\n",
+ dir==1 ? pos_d - comm->cell_x1[dim] : pos_d - comm->cell_x0[dim]);
+ if (bHaveLimitdAndCMOld)
+ {
+ fprintf(fplog,"Old coordinates: %8.3f %8.3f %8.3f\n",
+ cm_old[XX],cm_old[YY],cm_old[ZZ]);
+ }
+ fprintf(fplog,"New coordinates: %8.3f %8.3f %8.3f\n",
+ cm_new[XX],cm_new[YY],cm_new[ZZ]);
+ fprintf(fplog,"Old cell boundaries in direction %c: %8.3f %8.3f\n",
+ dim2char(dim),
+ comm->old_cell_x0[dim],comm->old_cell_x1[dim]);
+ fprintf(fplog,"New cell boundaries in direction %c: %8.3f %8.3f\n",
+ dim2char(dim),
+ comm->cell_x0[dim],comm->cell_x1[dim]);
+}
+
+static void cg_move_error(FILE *fplog,
+ gmx_domdec_t *dd,
+ gmx_large_int_t step,int cg,int dim,int dir,
+ gmx_bool bHaveLimitdAndCMOld,real limitd,
+ rvec cm_old,rvec cm_new,real pos_d)
+{
+ if (fplog)
+ {
+ print_cg_move(fplog, dd,step,cg,dim,dir,
+ bHaveLimitdAndCMOld,limitd,cm_old,cm_new,pos_d);
+ }
+ print_cg_move(stderr,dd,step,cg,dim,dir,
+ bHaveLimitdAndCMOld,limitd,cm_old,cm_new,pos_d);
+ gmx_fatal(FARGS,
+ "A charge group moved too far between two domain decomposition steps\n"
+ "This usually means that your system is not well equilibrated");
+}
+
+static void rotate_state_atom(t_state *state,int a)
+{
+ int est;
+
+ for(est=0; est<estNR; est++)
+ {
+ if (EST_DISTR(est) && state->flags & (1<<est)) {
+ switch (est) {
+ case estX:
+ /* Rotate the complete state; for a rectangular box only */
+ state->x[a][YY] = state->box[YY][YY] - state->x[a][YY];
+ state->x[a][ZZ] = state->box[ZZ][ZZ] - state->x[a][ZZ];
+ break;
+ case estV:
+ state->v[a][YY] = -state->v[a][YY];
+ state->v[a][ZZ] = -state->v[a][ZZ];
+ break;
+ case estSDX:
+ state->sd_X[a][YY] = -state->sd_X[a][YY];
+ state->sd_X[a][ZZ] = -state->sd_X[a][ZZ];
+ break;
+ case estCGP:
+ state->cg_p[a][YY] = -state->cg_p[a][YY];
+ state->cg_p[a][ZZ] = -state->cg_p[a][ZZ];
+ break;
+ case estDISRE_INITF:
+ case estDISRE_RM3TAV:
+ case estORIRE_INITF:
+ case estORIRE_DTAV:
+ /* These are distances, so not affected by rotation */
+ break;
+ default:
+ gmx_incons("Unknown state entry encountered in rotate_state_atom");
+ }
+ }
+ }
+}
+
+static int dd_redistribute_cg(FILE *fplog,gmx_large_int_t step,
+ gmx_domdec_t *dd,ivec tric_dir,
+ t_state *state,rvec **f,
+ t_forcerec *fr,t_mdatoms *md,
+ gmx_bool bCompact,
+ t_nrnb *nrnb)
+{
+ int *move;
+ int npbcdim;
+ int ncg[DIM*2],nat[DIM*2];
+ int c,i,cg,k,k0,k1,d,dim,dim2,dir,d2,d3,d4,cell_d;
+ int mc,cdd,nrcg,ncg_recv,nat_recv,nvs,nvr,nvec,vec;
+ int sbuf[2],rbuf[2];
+ int home_pos_cg,home_pos_at,ncg_stay_home,buf_pos;
+ int flag;
+ gmx_bool bV=FALSE,bSDX=FALSE,bCGP=FALSE;
+ gmx_bool bScrew;
+ ivec dev;
+ real inv_ncg,pos_d;
+ matrix tcm;
+ rvec *cg_cm,cell_x0,cell_x1,limitd,limit0,limit1,cm_new;
+ atom_id *cgindex;
+ cginfo_mb_t *cginfo_mb;
+ gmx_domdec_comm_t *comm;
+
+ if (dd->bScrewPBC)
+ {
+ check_screw_box(state->box);
+ }
+
+ comm = dd->comm;
+ cg_cm = fr->cg_cm;
+
+ for(i=0; i<estNR; i++)
+ {
+ if (EST_DISTR(i))
+ {
+ switch (i)
+ {
+ case estX: /* Always present */ break;
+ case estV: bV = (state->flags & (1<<i)); break;
+ case estSDX: bSDX = (state->flags & (1<<i)); break;
+ case estCGP: bCGP = (state->flags & (1<<i)); break;
+ case estLD_RNG:
+ case estLD_RNGI:
+ case estDISRE_INITF:
+ case estDISRE_RM3TAV:
+ case estORIRE_INITF:
+ case estORIRE_DTAV:
+ /* No processing required */
+ break;
+ default:
+ gmx_incons("Unknown state entry encountered in dd_redistribute_cg");
+ }
+ }
+ }
+
+ if (dd->ncg_tot > comm->nalloc_int)
+ {
+ comm->nalloc_int = over_alloc_dd(dd->ncg_tot);
+ srenew(comm->buf_int,comm->nalloc_int);
+ }
+ move = comm->buf_int;
+
+ /* Clear the count */
+ for(c=0; c<dd->ndim*2; c++)
+ {
+ ncg[c] = 0;
+ nat[c] = 0;
+ }
+
+ npbcdim = dd->npbcdim;
+
+ for(d=0; (d<DIM); d++)
+ {
+ limitd[d] = dd->comm->cellsize_min[d];
+ if (d >= npbcdim && dd->ci[d] == 0)
+ {
+ cell_x0[d] = -GMX_FLOAT_MAX;
+ }
+ else
+ {
+ cell_x0[d] = comm->cell_x0[d];
+ }
+ if (d >= npbcdim && dd->ci[d] == dd->nc[d] - 1)
+ {
+ cell_x1[d] = GMX_FLOAT_MAX;
+ }
+ else
+ {
+ cell_x1[d] = comm->cell_x1[d];
+ }
+ if (d < npbcdim)
+ {
+ limit0[d] = comm->old_cell_x0[d] - limitd[d];
+ limit1[d] = comm->old_cell_x1[d] + limitd[d];
+ }
+ else
+ {
+ /* We check after communication if a charge group moved
+ * more than one cell. Set the pre-comm check limit to float_max.
+ */
+ limit0[d] = -GMX_FLOAT_MAX;
+ limit1[d] = GMX_FLOAT_MAX;
+ }
+ }
+
+ make_tric_corr_matrix(npbcdim,state->box,tcm);
+
+ cgindex = dd->cgindex;
+
+ /* Compute the center of geometry for all home charge groups
+ * and put them in the box and determine where they should go.
+ */
+ for(cg=0; cg<dd->ncg_home; cg++)
+ {
+ k0 = cgindex[cg];
+ k1 = cgindex[cg+1];
+ nrcg = k1 - k0;
+ if (nrcg == 1)
+ {
+ copy_rvec(state->x[k0],cm_new);
+ }
+ else
+ {
+ inv_ncg = 1.0/nrcg;
+
+ clear_rvec(cm_new);
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_inc(cm_new,state->x[k]);
+ }
+ for(d=0; (d<DIM); d++)
+ {
+ cm_new[d] = inv_ncg*cm_new[d];
+ }
+ }
+
+ clear_ivec(dev);
+ /* Do pbc and check DD cell boundary crossings */
+ for(d=DIM-1; d>=0; d--)
+ {
+ if (dd->nc[d] > 1)
+ {
+ bScrew = (dd->bScrewPBC && d == XX);
+ /* Determine the location of this cg in lattice coordinates */
+ pos_d = cm_new[d];
+ if (tric_dir[d])
+ {
+ for(d2=d+1; d2<DIM; d2++)
+ {
+ pos_d += cm_new[d2]*tcm[d2][d];
+ }
+ }
+ /* Put the charge group in the triclinic unit-cell */
+ if (pos_d >= cell_x1[d])
+ {
+ if (pos_d >= limit1[d])
+ {
+ cg_move_error(fplog,dd,step,cg,d,1,TRUE,limitd[d],
+ cg_cm[cg],cm_new,pos_d);
+ }
+ dev[d] = 1;
+ if (dd->ci[d] == dd->nc[d] - 1)
+ {
+ rvec_dec(cm_new,state->box[d]);
+ if (bScrew)
+ {
+ cm_new[YY] = state->box[YY][YY] - cm_new[YY];
+ cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
+ }
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_dec(state->x[k],state->box[d]);
+ if (bScrew)
+ {
+ rotate_state_atom(state,k);
+ }
+ }
+ }
+ }
+ else if (pos_d < cell_x0[d])
+ {
+ if (pos_d < limit0[d])
+ {
+ cg_move_error(fplog,dd,step,cg,d,-1,TRUE,limitd[d],
+ cg_cm[cg],cm_new,pos_d);
+ }
+ dev[d] = -1;
+ if (dd->ci[d] == 0)
+ {
+ rvec_inc(cm_new,state->box[d]);
+ if (bScrew)
+ {
+ cm_new[YY] = state->box[YY][YY] - cm_new[YY];
+ cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
+ }
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_inc(state->x[k],state->box[d]);
+ if (bScrew)
+ {
+ rotate_state_atom(state,k);
+ }
+ }
+ }
+ }
+ }
+ else if (d < npbcdim)
+ {
+ /* Put the charge group in the rectangular unit-cell */
+ while (cm_new[d] >= state->box[d][d])
+ {
+ rvec_dec(cm_new,state->box[d]);
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_dec(state->x[k],state->box[d]);
+ }
+ }
+ while (cm_new[d] < 0)
+ {
+ rvec_inc(cm_new,state->box[d]);
+ for(k=k0; (k<k1); k++)
+ {
+ rvec_inc(state->x[k],state->box[d]);
+ }
+ }
+ }
+ }
+
+ copy_rvec(cm_new,cg_cm[cg]);
+
+ /* Determine where this cg should go */
+ flag = 0;
+ mc = -1;
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ if (dev[dim] == 1)
+ {
+ flag |= DD_FLAG_FW(d);
+ if (mc == -1)
+ {
+ mc = d*2;
+ }
+ }
+ else if (dev[dim] == -1)
+ {
+ flag |= DD_FLAG_BW(d);
+ if (mc == -1) {
+ if (dd->nc[dim] > 2)
+ {
+ mc = d*2 + 1;
+ }
+ else
+ {
+ mc = d*2;
+ }
+ }
+ }
+ }
+ move[cg] = mc;
+ if (mc >= 0)
+ {
+ if (ncg[mc]+1 > comm->cggl_flag_nalloc[mc])
+ {
+ comm->cggl_flag_nalloc[mc] = over_alloc_dd(ncg[mc]+1);
+ srenew(comm->cggl_flag[mc],comm->cggl_flag_nalloc[mc]*DD_CGIBS);
+ }
+ comm->cggl_flag[mc][ncg[mc]*DD_CGIBS ] = dd->index_gl[cg];
+ /* We store the cg size in the lower 16 bits
+ * and the place where the charge group should go
+ * in the next 6 bits. This saves some communication volume.
+ */
+ comm->cggl_flag[mc][ncg[mc]*DD_CGIBS+1] = nrcg | flag;
+ ncg[mc] += 1;
+ nat[mc] += nrcg;
+ }
+ }
+
+ inc_nrnb(nrnb,eNR_CGCM,dd->nat_home);
+ inc_nrnb(nrnb,eNR_RESETX,dd->ncg_home);
+
+ nvec = 1;
+ if (bV)
+ {
+ nvec++;
+ }
+ if (bSDX)
+ {
+ nvec++;
+ }
+ if (bCGP)
+ {
+ nvec++;
+ }
+
+ /* Make sure the communication buffers are large enough */
+ for(mc=0; mc<dd->ndim*2; mc++)
+ {
+ nvr = ncg[mc] + nat[mc]*nvec;
+ if (nvr > comm->cgcm_state_nalloc[mc])
+ {
+ comm->cgcm_state_nalloc[mc] = over_alloc_dd(nvr);
+ srenew(comm->cgcm_state[mc],comm->cgcm_state_nalloc[mc]);
+ }
+ }
+
+ /* Recalculating cg_cm might be cheaper than communicating,
+ * but that could give rise to rounding issues.
+ */
+ home_pos_cg =
+ compact_and_copy_vec_cg(dd->ncg_home,move,cgindex,
+ nvec,cg_cm,comm,bCompact);
+
+ vec = 0;
+ home_pos_at =
+ compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
+ nvec,vec++,state->x,comm,bCompact);
+ if (bV)
+ {
+ compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
+ nvec,vec++,state->v,comm,bCompact);
+ }
+ if (bSDX)
+ {
+ compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
+ nvec,vec++,state->sd_X,comm,bCompact);
+ }
+ if (bCGP)
+ {
+ compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
+ nvec,vec++,state->cg_p,comm,bCompact);
+ }
+
+ if (bCompact)
+ {
+ compact_ind(dd->ncg_home,move,
+ dd->index_gl,dd->cgindex,dd->gatindex,
+ dd->ga2la,comm->bLocalCG,
+ fr->cginfo);
+ }
+ else
+ {
+ clear_and_mark_ind(dd->ncg_home,move,
+ dd->index_gl,dd->cgindex,dd->gatindex,
+ dd->ga2la,comm->bLocalCG,
+ fr->ns.grid->cell_index);
+ }
+
+ cginfo_mb = fr->cginfo_mb;
+
+ ncg_stay_home = home_pos_cg;
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ ncg_recv = 0;
+ nat_recv = 0;
+ nvr = 0;
+ for(dir=0; dir<(dd->nc[dim]==2 ? 1 : 2); dir++)
+ {
+ cdd = d*2 + dir;
+ /* Communicate the cg and atom counts */
+ sbuf[0] = ncg[cdd];
+ sbuf[1] = nat[cdd];
+ if (debug)
+ {
+ fprintf(debug,"Sending ddim %d dir %d: ncg %d nat %d\n",
+ d,dir,sbuf[0],sbuf[1]);
+ }
+ dd_sendrecv_int(dd, d, dir, sbuf, 2, rbuf, 2);
+
+ if ((ncg_recv+rbuf[0])*DD_CGIBS > comm->nalloc_int)
+ {
+ comm->nalloc_int = over_alloc_dd((ncg_recv+rbuf[0])*DD_CGIBS);
+ srenew(comm->buf_int,comm->nalloc_int);
+ }
+
+ /* Communicate the charge group indices, sizes and flags */
+ dd_sendrecv_int(dd, d, dir,
+ comm->cggl_flag[cdd], sbuf[0]*DD_CGIBS,
+ comm->buf_int+ncg_recv*DD_CGIBS, rbuf[0]*DD_CGIBS);
+
+ nvs = ncg[cdd] + nat[cdd]*nvec;
+ i = rbuf[0] + rbuf[1] *nvec;
+ vec_rvec_check_alloc(&comm->vbuf,nvr+i);
+
+ /* Communicate cgcm and state */
+ dd_sendrecv_rvec(dd, d, dir,
+ comm->cgcm_state[cdd], nvs,
+ comm->vbuf.v+nvr, i);
+ ncg_recv += rbuf[0];
+ nat_recv += rbuf[1];
+ nvr += i;
+ }
+
+ /* Process the received charge groups */
+ buf_pos = 0;
+ for(cg=0; cg<ncg_recv; cg++)
+ {
+ flag = comm->buf_int[cg*DD_CGIBS+1];
+
+ if (dim >= npbcdim && dd->nc[dim] > 2)
+ {
+ /* No pbc in this dim and more than one domain boundary.
+ * We to a separate check if a charge did not move too far.
+ */
+ if (((flag & DD_FLAG_FW(d)) &&
+ comm->vbuf.v[buf_pos][d] > cell_x1[dim]) ||
+ ((flag & DD_FLAG_BW(d)) &&
+ comm->vbuf.v[buf_pos][d] < cell_x0[dim]))
+ {
+ cg_move_error(fplog,dd,step,cg,d,
+ (flag & DD_FLAG_FW(d)) ? 1 : 0,
+ FALSE,0,
+ comm->vbuf.v[buf_pos],
+ comm->vbuf.v[buf_pos],
+ comm->vbuf.v[buf_pos][d]);
+ }
+ }
+
+ mc = -1;
+ if (d < dd->ndim-1)
+ {
+ /* Check which direction this cg should go */
+ for(d2=d+1; (d2<dd->ndim && mc==-1); d2++)
+ {
+ if (dd->bGridJump)
+ {
+ /* The cell boundaries for dimension d2 are not equal
+ * for each cell row of the lower dimension(s),
+ * therefore we might need to redetermine where
+ * this cg should go.
+ */
+ dim2 = dd->dim[d2];
+ /* If this cg crosses the box boundary in dimension d2
+ * we can use the communicated flag, so we do not
+ * have to worry about pbc.
+ */
+ if (!((dd->ci[dim2] == dd->nc[dim2]-1 &&
+ (flag & DD_FLAG_FW(d2))) ||
+ (dd->ci[dim2] == 0 &&
+ (flag & DD_FLAG_BW(d2)))))
+ {
+ /* Clear the two flags for this dimension */
+ flag &= ~(DD_FLAG_FW(d2) | DD_FLAG_BW(d2));
+ /* Determine the location of this cg
+ * in lattice coordinates
+ */
+ pos_d = comm->vbuf.v[buf_pos][dim2];
+ if (tric_dir[dim2])
+ {
+ for(d3=dim2+1; d3<DIM; d3++)
+ {
+ pos_d +=
+ comm->vbuf.v[buf_pos][d3]*tcm[d3][dim2];
+ }
+ }
+ /* Check of we are not at the box edge.
+ * pbc is only handled in the first step above,
+ * but this check could move over pbc while
+ * the first step did not due to different rounding.
+ */
+ if (pos_d >= cell_x1[dim2] &&
+ dd->ci[dim2] != dd->nc[dim2]-1)
+ {
+ flag |= DD_FLAG_FW(d2);
+ }
+ else if (pos_d < cell_x0[dim2] &&
+ dd->ci[dim2] != 0)
+ {
+ flag |= DD_FLAG_BW(d2);
+ }
+ comm->buf_int[cg*DD_CGIBS+1] = flag;
+ }
+ }
+ /* Set to which neighboring cell this cg should go */
+ if (flag & DD_FLAG_FW(d2))
+ {
+ mc = d2*2;
+ }
+ else if (flag & DD_FLAG_BW(d2))
+ {
+ if (dd->nc[dd->dim[d2]] > 2)
+ {
+ mc = d2*2+1;
+ }
+ else
+ {
+ mc = d2*2;
+ }
+ }
+ }
+ }
+
+ nrcg = flag & DD_FLAG_NRCG;
+ if (mc == -1)
+ {
+ if (home_pos_cg+1 > dd->cg_nalloc)
+ {
+ dd->cg_nalloc = over_alloc_dd(home_pos_cg+1);
+ srenew(dd->index_gl,dd->cg_nalloc);
+ srenew(dd->cgindex,dd->cg_nalloc+1);
+ }
+ /* Set the global charge group index and size */
+ dd->index_gl[home_pos_cg] = comm->buf_int[cg*DD_CGIBS];
+ dd->cgindex[home_pos_cg+1] = dd->cgindex[home_pos_cg] + nrcg;
+ /* Copy the state from the buffer */
+ if (home_pos_cg >= fr->cg_nalloc)
+ {
+ dd_realloc_fr_cg(fr,home_pos_cg+1);
+ cg_cm = fr->cg_cm;
+ }
+ copy_rvec(comm->vbuf.v[buf_pos++],cg_cm[home_pos_cg]);
+ /* Set the cginfo */
+ fr->cginfo[home_pos_cg] = ddcginfo(cginfo_mb,
+ dd->index_gl[home_pos_cg]);
+ if (comm->bLocalCG)
+ {
+ comm->bLocalCG[dd->index_gl[home_pos_cg]] = TRUE;
+ }
+
+ if (home_pos_at+nrcg > state->nalloc)
+ {
+ dd_realloc_state(state,f,home_pos_at+nrcg);
+ }
+ for(i=0; i<nrcg; i++)
+ {
+ copy_rvec(comm->vbuf.v[buf_pos++],
+ state->x[home_pos_at+i]);
+ }
+ if (bV)
+ {
+ for(i=0; i<nrcg; i++)
+ {
+ copy_rvec(comm->vbuf.v[buf_pos++],
+ state->v[home_pos_at+i]);
+ }
+ }
+ if (bSDX)
+ {
+ for(i=0; i<nrcg; i++)
+ {
+ copy_rvec(comm->vbuf.v[buf_pos++],
+ state->sd_X[home_pos_at+i]);
+ }
+ }
+ if (bCGP)
+ {
+ for(i=0; i<nrcg; i++)
+ {
+ copy_rvec(comm->vbuf.v[buf_pos++],
+ state->cg_p[home_pos_at+i]);
+ }
+ }
+ home_pos_cg += 1;
+ home_pos_at += nrcg;
+ }
+ else
+ {
+ /* Reallocate the buffers if necessary */
+ if (ncg[mc]+1 > comm->cggl_flag_nalloc[mc])
+ {
+ comm->cggl_flag_nalloc[mc] = over_alloc_dd(ncg[mc]+1);
+ srenew(comm->cggl_flag[mc],comm->cggl_flag_nalloc[mc]*DD_CGIBS);
+ }
+ nvr = ncg[mc] + nat[mc]*nvec;
+ if (nvr + 1 + nrcg*nvec > comm->cgcm_state_nalloc[mc])
+ {
+ comm->cgcm_state_nalloc[mc] = over_alloc_dd(nvr + 1 + nrcg*nvec);
+ srenew(comm->cgcm_state[mc],comm->cgcm_state_nalloc[mc]);
+ }
+ /* Copy from the receive to the send buffers */
+ memcpy(comm->cggl_flag[mc] + ncg[mc]*DD_CGIBS,
+ comm->buf_int + cg*DD_CGIBS,
+ DD_CGIBS*sizeof(int));
+ memcpy(comm->cgcm_state[mc][nvr],
+ comm->vbuf.v[buf_pos],
+ (1+nrcg*nvec)*sizeof(rvec));
+ buf_pos += 1 + nrcg*nvec;
+ ncg[mc] += 1;
+ nat[mc] += nrcg;
+ }
+ }
+ }
+
+ /* With sorting (!bCompact) the indices are now only partially up to date
+ * and ncg_home and nat_home are not the real count, since there are
+ * "holes" in the arrays for the charge groups that moved to neighbors.
+ */
+ dd->ncg_home = home_pos_cg;
+ dd->nat_home = home_pos_at;
+
+ if (debug)
+ {
+ fprintf(debug,"Finished repartitioning\n");
+ }
+
+ return ncg_stay_home;
+}
+
+void dd_cycles_add(gmx_domdec_t *dd,float cycles,int ddCycl)
+{
+ dd->comm->cycl[ddCycl] += cycles;
+ dd->comm->cycl_n[ddCycl]++;
+ if (cycles > dd->comm->cycl_max[ddCycl])
+ {
+ dd->comm->cycl_max[ddCycl] = cycles;
+ }
+}
+
+static double force_flop_count(t_nrnb *nrnb)
+{
+ int i;
+ double sum;
+ const char *name;
+
+ sum = 0;
+ for(i=eNR_NBKERNEL010; i<eNR_NBKERNEL_FREE_ENERGY; i++)
+ {
+ /* To get closer to the real timings, we half the count
+ * for the normal loops and again half it for water loops.
+ */
+ name = nrnb_str(i);
+ if (strstr(name,"W3") != NULL || strstr(name,"W4") != NULL)
+ {
+ sum += nrnb->n[i]*0.25*cost_nrnb(i);
+ }
+ else
+ {
+ sum += nrnb->n[i]*0.50*cost_nrnb(i);
+ }
+ }
+ for(i=eNR_NBKERNEL_FREE_ENERGY; i<=eNR_NB14; i++)
+ {
+ name = nrnb_str(i);
+ if (strstr(name,"W3") != NULL || strstr(name,"W4") != NULL)
+ sum += nrnb->n[i]*cost_nrnb(i);
+ }
+ for(i=eNR_BONDS; i<=eNR_WALLS; i++)
+ {
+ sum += nrnb->n[i]*cost_nrnb(i);
+ }
+
+ return sum;
+}
+
+void dd_force_flop_start(gmx_domdec_t *dd,t_nrnb *nrnb)
+{
+ if (dd->comm->eFlop)
+ {
+ dd->comm->flop -= force_flop_count(nrnb);
+ }
+}
+void dd_force_flop_stop(gmx_domdec_t *dd,t_nrnb *nrnb)
+{
+ if (dd->comm->eFlop)
+ {
+ dd->comm->flop += force_flop_count(nrnb);
+ dd->comm->flop_n++;
+ }
+}
+
+static void clear_dd_cycle_counts(gmx_domdec_t *dd)
+{
+ int i;
+
+ for(i=0; i<ddCyclNr; i++)
+ {
+ dd->comm->cycl[i] = 0;
+ dd->comm->cycl_n[i] = 0;
+ dd->comm->cycl_max[i] = 0;
+ }
+ dd->comm->flop = 0;
+ dd->comm->flop_n = 0;
+}
+
+static void get_load_distribution(gmx_domdec_t *dd,gmx_wallcycle_t wcycle)
+{
+ gmx_domdec_comm_t *comm;
+ gmx_domdec_load_t *load;
+ gmx_domdec_root_t *root=NULL;
+ int d,dim,cid,i,pos;
+ float cell_frac=0,sbuf[DD_NLOAD_MAX];
+ gmx_bool bSepPME;
+
+ if (debug)
+ {
+ fprintf(debug,"get_load_distribution start\n");
+ }
+
+ wallcycle_start(wcycle,ewcDDCOMMLOAD);
+
+ comm = dd->comm;
+
+ bSepPME = (dd->pme_nodeid >= 0);
+
+ for(d=dd->ndim-1; d>=0; d--)
+ {
+ dim = dd->dim[d];
+ /* Check if we participate in the communication in this dimension */
+ if (d == dd->ndim-1 ||
+ (dd->ci[dd->dim[d+1]]==0 && dd->ci[dd->dim[dd->ndim-1]]==0))
+ {
+ load = &comm->load[d];
+ if (dd->bGridJump)
+ {
+ cell_frac = comm->cell_f1[d] - comm->cell_f0[d];
+ }
+ pos = 0;
+ if (d == dd->ndim-1)
+ {
+ sbuf[pos++] = dd_force_load(comm);
+ sbuf[pos++] = sbuf[0];
+ if (dd->bGridJump)
+ {
+ sbuf[pos++] = sbuf[0];
+ sbuf[pos++] = cell_frac;
+ if (d > 0)
+ {
+ sbuf[pos++] = comm->cell_f_max0[d];
+ sbuf[pos++] = comm->cell_f_min1[d];
+ }
+ }
+ if (bSepPME)
+ {
+ sbuf[pos++] = comm->cycl[ddCyclPPduringPME];
+ sbuf[pos++] = comm->cycl[ddCyclPME];
+ }
+ }
+ else
+ {
+ sbuf[pos++] = comm->load[d+1].sum;
+ sbuf[pos++] = comm->load[d+1].max;
+ if (dd->bGridJump)
+ {
+ sbuf[pos++] = comm->load[d+1].sum_m;
+ sbuf[pos++] = comm->load[d+1].cvol_min*cell_frac;
+ sbuf[pos++] = comm->load[d+1].flags;
+ if (d > 0)
+ {
+ sbuf[pos++] = comm->cell_f_max0[d];
+ sbuf[pos++] = comm->cell_f_min1[d];
+ }
+ }
+ if (bSepPME)
+ {
+ sbuf[pos++] = comm->load[d+1].mdf;
+ sbuf[pos++] = comm->load[d+1].pme;
+ }
+ }
+ load->nload = pos;
+ /* Communicate a row in DD direction d.
+ * The communicators are setup such that the root always has rank 0.
+ */
+#ifdef GMX_MPI
+ MPI_Gather(sbuf ,load->nload*sizeof(float),MPI_BYTE,
+ load->load,load->nload*sizeof(float),MPI_BYTE,
+ 0,comm->mpi_comm_load[d]);
+#endif
+ if (dd->ci[dim] == dd->master_ci[dim])
+ {
+ /* We are the root, process this row */
+ if (comm->bDynLoadBal)
+ {
+ root = comm->root[d];
+ }
+ load->sum = 0;
+ load->max = 0;
+ load->sum_m = 0;
+ load->cvol_min = 1;
+ load->flags = 0;
+ load->mdf = 0;
+ load->pme = 0;
+ pos = 0;
+ for(i=0; i<dd->nc[dim]; i++)
+ {
+ load->sum += load->load[pos++];
+ load->max = max(load->max,load->load[pos]);
+ pos++;
+ if (dd->bGridJump)
+ {
+ if (root->bLimited)
+ {
+ /* This direction could not be load balanced properly,
+ * therefore we need to use the maximum iso the average load.
+ */
+ load->sum_m = max(load->sum_m,load->load[pos]);
+ }
+ else
+ {
+ load->sum_m += load->load[pos];
+ }
+ pos++;
+ load->cvol_min = min(load->cvol_min,load->load[pos]);
+ pos++;
+ if (d < dd->ndim-1)
+ {
+ load->flags = (int)(load->load[pos++] + 0.5);
+ }
+ if (d > 0)
+ {
+ root->cell_f_max0[i] = load->load[pos++];
+ root->cell_f_min1[i] = load->load[pos++];
+ }
+ }
+ if (bSepPME)
+ {
+ load->mdf = max(load->mdf,load->load[pos]);
+ pos++;
+ load->pme = max(load->pme,load->load[pos]);
+ pos++;
+ }
+ }
+ if (comm->bDynLoadBal && root->bLimited)
+ {
+ load->sum_m *= dd->nc[dim];
+ load->flags |= (1<<d);
+ }
+ }
+ }
+ }
+
+ if (DDMASTER(dd))
+ {
+ comm->nload += dd_load_count(comm);
+ comm->load_step += comm->cycl[ddCyclStep];
+ comm->load_sum += comm->load[0].sum;
+ comm->load_max += comm->load[0].max;
+ if (comm->bDynLoadBal)
+ {
+ for(d=0; d<dd->ndim; d++)
+ {
+ if (comm->load[0].flags & (1<<d))
+ {
+ comm->load_lim[d]++;
+ }
+ }
+ }
+ if (bSepPME)
+ {
+ comm->load_mdf += comm->load[0].mdf;
+ comm->load_pme += comm->load[0].pme;
+ }
+ }
+
+ wallcycle_stop(wcycle,ewcDDCOMMLOAD);
+
+ if (debug)
+ {
+ fprintf(debug,"get_load_distribution finished\n");
+ }
+}
+
+static float dd_force_imb_perf_loss(gmx_domdec_t *dd)
+{
+ /* Return the relative performance loss on the total run time
+ * due to the force calculation load imbalance.
+ */
+ if (dd->comm->nload > 0)
+ {
+ return
+ (dd->comm->load_max*dd->nnodes - dd->comm->load_sum)/
+ (dd->comm->load_step*dd->nnodes);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static void print_dd_load_av(FILE *fplog,gmx_domdec_t *dd)
+{
+ char buf[STRLEN];
+ int npp,npme,nnodes,d,limp;
+ float imbal,pme_f_ratio,lossf,lossp=0;
+ gmx_bool bLim;
+ gmx_domdec_comm_t *comm;
+
+ comm = dd->comm;
+ if (DDMASTER(dd) && comm->nload > 0)
+ {
+ npp = dd->nnodes;
+ npme = (dd->pme_nodeid >= 0) ? comm->npmenodes : 0;
+ nnodes = npp + npme;
+ imbal = comm->load_max*npp/comm->load_sum - 1;
+ lossf = dd_force_imb_perf_loss(dd);
+ sprintf(buf," Average load imbalance: %.1f %%\n",imbal*100);
+ fprintf(fplog,"%s",buf);
+ fprintf(stderr,"\n");
+ fprintf(stderr,"%s",buf);
+ sprintf(buf," Part of the total run time spent waiting due to load imbalance: %.1f %%\n",lossf*100);
+ fprintf(fplog,"%s",buf);
+ fprintf(stderr,"%s",buf);
+ bLim = FALSE;
+ if (comm->bDynLoadBal)
+ {
+ sprintf(buf," Steps where the load balancing was limited by -rdd, -rcon and/or -dds:");
+ for(d=0; d<dd->ndim; d++)
+ {
+ limp = (200*comm->load_lim[d]+1)/(2*comm->nload);
+ sprintf(buf+strlen(buf)," %c %d %%",dim2char(dd->dim[d]),limp);
+ if (limp >= 50)
+ {
+ bLim = TRUE;
+ }
+ }
+ sprintf(buf+strlen(buf),"\n");
+ fprintf(fplog,"%s",buf);
+ fprintf(stderr,"%s",buf);
+ }
+ if (npme > 0)
+ {
+ pme_f_ratio = comm->load_pme/comm->load_mdf;
+ lossp = (comm->load_pme -comm->load_mdf)/comm->load_step;
+ if (lossp <= 0)
+ {
+ lossp *= (float)npme/(float)nnodes;
+ }
+ else
+ {
+ lossp *= (float)npp/(float)nnodes;
+ }
+ sprintf(buf," Average PME mesh/force load: %5.3f\n",pme_f_ratio);
+ fprintf(fplog,"%s",buf);
+ fprintf(stderr,"%s",buf);
+ sprintf(buf," Part of the total run time spent waiting due to PP/PME imbalance: %.1f %%\n",fabs(lossp)*100);
+ fprintf(fplog,"%s",buf);
+ fprintf(stderr,"%s",buf);
+ }
+ fprintf(fplog,"\n");
+ fprintf(stderr,"\n");
+
+ if (lossf >= DD_PERF_LOSS)
+ {
+ sprintf(buf,
+ "NOTE: %.1f %% performance was lost due to load imbalance\n"
+ " in the domain decomposition.\n",lossf*100);
+ if (!comm->bDynLoadBal)
+ {
+ sprintf(buf+strlen(buf)," You might want to use dynamic load balancing (option -dlb.)\n");
+ }
+ else if (bLim)
+ {
+ sprintf(buf+strlen(buf)," You might want to decrease the cell size limit (options -rdd, -rcon and/or -dds).\n");
+ }
+ fprintf(fplog,"%s\n",buf);
+ fprintf(stderr,"%s\n",buf);
+ }
+ if (npme > 0 && fabs(lossp) >= DD_PERF_LOSS)
+ {
+ sprintf(buf,
+ "NOTE: %.1f %% performance was lost because the PME nodes\n"
+ " had %s work to do than the PP nodes.\n"
+ " You might want to %s the number of PME nodes\n"
+ " or %s the cut-off and the grid spacing.\n",
+ fabs(lossp*100),
+ (lossp < 0) ? "less" : "more",
+ (lossp < 0) ? "decrease" : "increase",
+ (lossp < 0) ? "decrease" : "increase");
+ fprintf(fplog,"%s\n",buf);
+ fprintf(stderr,"%s\n",buf);
+ }
+ }
+}
+
+static float dd_vol_min(gmx_domdec_t *dd)
+{
+ return dd->comm->load[0].cvol_min*dd->nnodes;
+}
+
+static gmx_bool dd_load_flags(gmx_domdec_t *dd)
+{
+ return dd->comm->load[0].flags;
+}
+
+static float dd_f_imbal(gmx_domdec_t *dd)
+{
+ return dd->comm->load[0].max*dd->nnodes/dd->comm->load[0].sum - 1;
+}
+
+static float dd_pme_f_ratio(gmx_domdec_t *dd)
+{
+ return dd->comm->load[0].pme/dd->comm->load[0].mdf;
+}
+
+static void dd_print_load(FILE *fplog,gmx_domdec_t *dd,gmx_large_int_t step)
+{
+ int flags,d;
+ char buf[22];
+
+ flags = dd_load_flags(dd);
+ if (flags)
+ {
+ fprintf(fplog,
+ "DD load balancing is limited by minimum cell size in dimension");
+ for(d=0; d<dd->ndim; d++)
+ {
+ if (flags & (1<<d))
+ {
+ fprintf(fplog," %c",dim2char(dd->dim[d]));
+ }
+ }
+ fprintf(fplog,"\n");
+ }
+ fprintf(fplog,"DD step %s",gmx_step_str(step,buf));
+ if (dd->comm->bDynLoadBal)
+ {
+ fprintf(fplog," vol min/aver %5.3f%c",
+ dd_vol_min(dd),flags ? '!' : ' ');
+ }
+ fprintf(fplog," load imb.: force %4.1f%%",dd_f_imbal(dd)*100);
+ if (dd->comm->cycl_n[ddCyclPME])
+ {
+ fprintf(fplog," pme mesh/force %5.3f",dd_pme_f_ratio(dd));
+ }
+ fprintf(fplog,"\n\n");
+}
+
+static void dd_print_load_verbose(gmx_domdec_t *dd)
+{
+ if (dd->comm->bDynLoadBal)
+ {
+ fprintf(stderr,"vol %4.2f%c ",
+ dd_vol_min(dd),dd_load_flags(dd) ? '!' : ' ');
+ }
+ fprintf(stderr,"imb F %2d%% ",(int)(dd_f_imbal(dd)*100+0.5));
+ if (dd->comm->cycl_n[ddCyclPME])
+ {
+ fprintf(stderr,"pme/F %4.2f ",dd_pme_f_ratio(dd));
+ }
+}
+
+#ifdef GMX_MPI
+static void make_load_communicator(gmx_domdec_t *dd,MPI_Group g_all,
+ int dim_ind,ivec loc)
+{
+ MPI_Group g_row;
+ MPI_Comm c_row;
+ int dim,i,*rank;
+ ivec loc_c;
+ gmx_domdec_root_t *root;
+
+ dim = dd->dim[dim_ind];
+ copy_ivec(loc,loc_c);
+ snew(rank,dd->nc[dim]);
+ for(i=0; i<dd->nc[dim]; i++)
+ {
+ loc_c[dim] = i;
+ rank[i] = dd_index(dd->nc,loc_c);
+ }
+ /* Here we create a new group, that does not necessarily
+ * include our process. But MPI_Comm_create needs to be
+ * called by all the processes in the original communicator.
+ * Calling MPI_Group_free afterwards gives errors, so I assume
+ * also the group is needed by all processes. (B. Hess)
+ */
+ MPI_Group_incl(g_all,dd->nc[dim],rank,&g_row);
+ MPI_Comm_create(dd->mpi_comm_all,g_row,&c_row);
+ if (c_row != MPI_COMM_NULL)
+ {
+ /* This process is part of the group */
+ dd->comm->mpi_comm_load[dim_ind] = c_row;
+ if (dd->comm->eDLB != edlbNO)
+ {
+ if (dd->ci[dim] == dd->master_ci[dim])
+ {
+ /* This is the root process of this row */
+ snew(dd->comm->root[dim_ind],1);
+ root = dd->comm->root[dim_ind];
+ snew(root->cell_f,DD_CELL_F_SIZE(dd,dim_ind));
+ snew(root->old_cell_f,dd->nc[dim]+1);
+ snew(root->bCellMin,dd->nc[dim]);
+ if (dim_ind > 0)
+ {
+ snew(root->cell_f_max0,dd->nc[dim]);
+ snew(root->cell_f_min1,dd->nc[dim]);
+ snew(root->bound_min,dd->nc[dim]);
+ snew(root->bound_max,dd->nc[dim]);
+ }
+ snew(root->buf_ncd,dd->nc[dim]);
+ }
+ else
+ {
+ /* This is not a root process, we only need to receive cell_f */
+ snew(dd->comm->cell_f_row,DD_CELL_F_SIZE(dd,dim_ind));
+ }
+ }
+ if (dd->ci[dim] == dd->master_ci[dim])
+ {
+ snew(dd->comm->load[dim_ind].load,dd->nc[dim]*DD_NLOAD_MAX);
+ }
+ }
+ sfree(rank);
+}
+#endif
+
+static void make_load_communicators(gmx_domdec_t *dd)
+{
+#ifdef GMX_MPI
+ MPI_Group g_all;
+ int dim0,dim1,i,j;
+ ivec loc;
+
+ if (debug)
+ fprintf(debug,"Making load communicators\n");
+
+ MPI_Comm_group(dd->mpi_comm_all,&g_all);
+
+ snew(dd->comm->load,dd->ndim);
+ snew(dd->comm->mpi_comm_load,dd->ndim);
+
+ clear_ivec(loc);
+ make_load_communicator(dd,g_all,0,loc);
+ if (dd->ndim > 1) {
+ dim0 = dd->dim[0];
+ for(i=0; i<dd->nc[dim0]; i++) {
+ loc[dim0] = i;
+ make_load_communicator(dd,g_all,1,loc);
+ }
+ }
+ if (dd->ndim > 2) {
+ dim0 = dd->dim[0];
+ for(i=0; i<dd->nc[dim0]; i++) {
+ loc[dim0] = i;
+ dim1 = dd->dim[1];
+ for(j=0; j<dd->nc[dim1]; j++) {
+ loc[dim1] = j;
+ make_load_communicator(dd,g_all,2,loc);
+ }
+ }
+ }
+
+ MPI_Group_free(&g_all);
+
+ if (debug)
+ fprintf(debug,"Finished making load communicators\n");
+#endif
+}
+
+void setup_dd_grid(FILE *fplog,gmx_domdec_t *dd)
+{
+ gmx_bool bZYX;
+ int d,dim,i,j,m;
+ ivec tmp,s;
+ int nzone,nzonep;
+ ivec dd_zp[DD_MAXIZONE];
+ gmx_domdec_zones_t *zones;
+ gmx_domdec_ns_ranges_t *izone;
+
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ copy_ivec(dd->ci,tmp);
+ tmp[dim] = (tmp[dim] + 1) % dd->nc[dim];
+ dd->neighbor[d][0] = ddcoord2ddnodeid(dd,tmp);
+ copy_ivec(dd->ci,tmp);
+ tmp[dim] = (tmp[dim] - 1 + dd->nc[dim]) % dd->nc[dim];
+ dd->neighbor[d][1] = ddcoord2ddnodeid(dd,tmp);
+ if (debug)
+ {
+ fprintf(debug,"DD rank %d neighbor ranks in dir %d are + %d - %d\n",
+ dd->rank,dim,
+ dd->neighbor[d][0],
+ dd->neighbor[d][1]);
+ }
+ }
+
+ if (DDMASTER(dd))
+ {
+ fprintf(stderr,"Making %dD domain decomposition %d x %d x %d\n",
+ dd->ndim,dd->nc[XX],dd->nc[YY],dd->nc[ZZ]);
+ }
+ if (fplog)
+ {
+ fprintf(fplog,"\nMaking %dD domain decomposition grid %d x %d x %d, home cell index %d %d %d\n\n",
+ dd->ndim,
+ dd->nc[XX],dd->nc[YY],dd->nc[ZZ],
+ dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
+ }
+ switch (dd->ndim)
+ {
+ case 3:
+ nzone = dd_z3n;
+ nzonep = dd_zp3n;
+ for(i=0; i<nzonep; i++)
+ {
+ copy_ivec(dd_zp3[i],dd_zp[i]);
+ }
+ break;
+ case 2:
+ nzone = dd_z2n;
+ nzonep = dd_zp2n;
+ for(i=0; i<nzonep; i++)
+ {
+ copy_ivec(dd_zp2[i],dd_zp[i]);
+ }
+ break;
+ case 1:
+ nzone = dd_z1n;
+ nzonep = dd_zp1n;
+ for(i=0; i<nzonep; i++)
+ {
+ copy_ivec(dd_zp1[i],dd_zp[i]);
+ }
+ break;
+ default:
+ gmx_fatal(FARGS,"Can only do 1, 2 or 3D domain decomposition");
+ nzone = 0;
+ nzonep = 0;
+ }
+
+ zones = &dd->comm->zones;
+
+ for(i=0; i<nzone; i++)
+ {
+ m = 0;
+ clear_ivec(zones->shift[i]);
+ for(d=0; d<dd->ndim; d++)
+ {
+ zones->shift[i][dd->dim[d]] = dd_zo[i][m++];
+ }
+ }
+
+ zones->n = nzone;
+ for(i=0; i<nzone; i++)
+ {
+ for(d=0; d<DIM; d++)
+ {
+ s[d] = dd->ci[d] - zones->shift[i][d];
+ if (s[d] < 0)
+ {
+ s[d] += dd->nc[d];
+ }
+ else if (s[d] >= dd->nc[d])
+ {
+ s[d] -= dd->nc[d];
+ }
+ }
+ }
+ zones->nizone = nzonep;
+ for(i=0; i<zones->nizone; i++)
+ {
+ if (dd_zp[i][0] != i)
+ {
+ gmx_fatal(FARGS,"Internal inconsistency in the dd grid setup");
+ }
+ izone = &zones->izone[i];
+ izone->j0 = dd_zp[i][1];
+ izone->j1 = dd_zp[i][2];
+ for(dim=0; dim<DIM; dim++)
+ {
+ if (dd->nc[dim] == 1)
+ {
+ /* All shifts should be allowed */
+ izone->shift0[dim] = -1;
+ izone->shift1[dim] = 1;
+ }
+ else
+ {
+ /*
+ izone->shift0[d] = 0;
+ izone->shift1[d] = 0;
+ for(j=izone->j0; j<izone->j1; j++) {
+ if (dd->shift[j][d] > dd->shift[i][d])
+ izone->shift0[d] = -1;
+ if (dd->shift[j][d] < dd->shift[i][d])
+ izone->shift1[d] = 1;
+ }
+ */
+
+ int shift_diff;
+
+ /* Assume the shift are not more than 1 cell */
+ izone->shift0[dim] = 1;
+ izone->shift1[dim] = -1;
+ for(j=izone->j0; j<izone->j1; j++)
+ {
+ shift_diff = zones->shift[j][dim] - zones->shift[i][dim];
+ if (shift_diff < izone->shift0[dim])
+ {
+ izone->shift0[dim] = shift_diff;
+ }
+ if (shift_diff > izone->shift1[dim])
+ {
+ izone->shift1[dim] = shift_diff;
+ }
+ }
+ }
+ }
+ }
+
+ if (dd->comm->eDLB != edlbNO)
+ {
+ snew(dd->comm->root,dd->ndim);
+ }
+
+ if (dd->comm->bRecordLoad)
+ {
+ make_load_communicators(dd);
+ }
+}
+
+static void make_pp_communicator(FILE *fplog,t_commrec *cr,int reorder)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ int i,rank,*buf;
+ ivec periods;
+#ifdef GMX_MPI
+ MPI_Comm comm_cart;
+#endif
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+#ifdef GMX_MPI
+ if (comm->bCartesianPP)
+ {
+ /* Set up cartesian communication for the particle-particle part */
+ if (fplog)
+ {
+ fprintf(fplog,"Will use a Cartesian communicator: %d x %d x %d\n",
+ dd->nc[XX],dd->nc[YY],dd->nc[ZZ]);
+ }
+
+ for(i=0; i<DIM; i++)
+ {
+ periods[i] = TRUE;
+ }
+ MPI_Cart_create(cr->mpi_comm_mygroup,DIM,dd->nc,periods,reorder,
+ &comm_cart);
+ /* We overwrite the old communicator with the new cartesian one */
+ cr->mpi_comm_mygroup = comm_cart;
+ }
+
+ dd->mpi_comm_all = cr->mpi_comm_mygroup;
+ MPI_Comm_rank(dd->mpi_comm_all,&dd->rank);
+
+ if (comm->bCartesianPP_PME)
+ {
+ /* Since we want to use the original cartesian setup for sim,
+ * and not the one after split, we need to make an index.
+ */
+ snew(comm->ddindex2ddnodeid,dd->nnodes);
+ comm->ddindex2ddnodeid[dd_index(dd->nc,dd->ci)] = dd->rank;
+ gmx_sumi(dd->nnodes,comm->ddindex2ddnodeid,cr);
+ /* Get the rank of the DD master,
+ * above we made sure that the master node is a PP node.
+ */
+ if (MASTER(cr))
+ {
+ rank = dd->rank;
+ }
+ else
+ {
+ rank = 0;
+ }
+ MPI_Allreduce(&rank,&dd->masterrank,1,MPI_INT,MPI_SUM,dd->mpi_comm_all);
+ }
+ else if (comm->bCartesianPP)
+ {
+ if (cr->npmenodes == 0)
+ {
+ /* The PP communicator is also
+ * the communicator for this simulation
+ */
+ cr->mpi_comm_mysim = cr->mpi_comm_mygroup;
+ }
+ cr->nodeid = dd->rank;
+
+ MPI_Cart_coords(dd->mpi_comm_all,dd->rank,DIM,dd->ci);
+
+ /* We need to make an index to go from the coordinates
+ * to the nodeid of this simulation.
+ */
+ snew(comm->ddindex2simnodeid,dd->nnodes);
+ snew(buf,dd->nnodes);
+ if (cr->duty & DUTY_PP)
+ {
+ buf[dd_index(dd->nc,dd->ci)] = cr->sim_nodeid;
+ }
+ /* Communicate the ddindex to simulation nodeid index */
+ MPI_Allreduce(buf,comm->ddindex2simnodeid,dd->nnodes,MPI_INT,MPI_SUM,
+ cr->mpi_comm_mysim);
+ sfree(buf);
+
+ /* Determine the master coordinates and rank.
+ * The DD master should be the same node as the master of this sim.
+ */
+ for(i=0; i<dd->nnodes; i++)
+ {
+ if (comm->ddindex2simnodeid[i] == 0)
+ {
+ ddindex2xyz(dd->nc,i,dd->master_ci);
+ MPI_Cart_rank(dd->mpi_comm_all,dd->master_ci,&dd->masterrank);
+ }
+ }
+ if (debug)
+ {
+ fprintf(debug,"The master rank is %d\n",dd->masterrank);
+ }
+ }
+ else
+ {
+ /* No Cartesian communicators */
+ /* We use the rank in dd->comm->all as DD index */
+ ddindex2xyz(dd->nc,dd->rank,dd->ci);
+ /* The simulation master nodeid is 0, so the DD master rank is also 0 */
+ dd->masterrank = 0;
+ clear_ivec(dd->master_ci);
+ }
+#endif
+
+ if (fplog)
+ {
+ fprintf(fplog,
+ "Domain decomposition nodeid %d, coordinates %d %d %d\n\n",
+ dd->rank,dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
+ }
+ if (debug)
+ {
+ fprintf(debug,
+ "Domain decomposition nodeid %d, coordinates %d %d %d\n\n",
+ dd->rank,dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
+ }
+}
+
+static void receive_ddindex2simnodeid(t_commrec *cr)
+{
+ gmx_domdec_t *dd;
+
+ gmx_domdec_comm_t *comm;
+ int *buf;
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+#ifdef GMX_MPI
+ if (!comm->bCartesianPP_PME && comm->bCartesianPP)
+ {
+ snew(comm->ddindex2simnodeid,dd->nnodes);
+ snew(buf,dd->nnodes);
+ if (cr->duty & DUTY_PP)
+ {
+ buf[dd_index(dd->nc,dd->ci)] = cr->sim_nodeid;
+ }
+#ifdef GMX_MPI
+ /* Communicate the ddindex to simulation nodeid index */
+ MPI_Allreduce(buf,comm->ddindex2simnodeid,dd->nnodes,MPI_INT,MPI_SUM,
+ cr->mpi_comm_mysim);
+#endif
+ sfree(buf);
+ }
+#endif
+}
+
+static gmx_domdec_master_t *init_gmx_domdec_master_t(gmx_domdec_t *dd,
+ int ncg,int natoms)
+{
+ gmx_domdec_master_t *ma;
+ int i;
+
+ snew(ma,1);
+
+ snew(ma->ncg,dd->nnodes);
+ snew(ma->index,dd->nnodes+1);
+ snew(ma->cg,ncg);
+ snew(ma->nat,dd->nnodes);
+ snew(ma->ibuf,dd->nnodes*2);
+ snew(ma->cell_x,DIM);
+ for(i=0; i<DIM; i++)
+ {
+ snew(ma->cell_x[i],dd->nc[i]+1);
+ }
+
+ if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
+ {
+ ma->vbuf = NULL;
+ }
+ else
+ {
+ snew(ma->vbuf,natoms);
+ }
+
+ return ma;
+}
+
+static void split_communicator(FILE *fplog,t_commrec *cr,int dd_node_order,
+ int reorder)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ int i,rank;
+ gmx_bool bDiv[DIM];
+ ivec periods;
+#ifdef GMX_MPI
+ MPI_Comm comm_cart;
+#endif
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+ if (comm->bCartesianPP)
+ {
+ for(i=1; i<DIM; i++)
+ {
+ bDiv[i] = ((cr->npmenodes*dd->nc[i]) % (dd->nnodes) == 0);
+ }
+ if (bDiv[YY] || bDiv[ZZ])
+ {
+ comm->bCartesianPP_PME = TRUE;
+ /* If we have 2D PME decomposition, which is always in x+y,
+ * we stack the PME only nodes in z.
+ * Otherwise we choose the direction that provides the thinnest slab
+ * of PME only nodes as this will have the least effect
+ * on the PP communication.
+ * But for the PME communication the opposite might be better.
+ */
+ if (bDiv[ZZ] && (comm->npmenodes_y > 1 ||
+ !bDiv[YY] ||
+ dd->nc[YY] > dd->nc[ZZ]))
+ {
+ comm->cartpmedim = ZZ;
+ }
+ else
+ {
+ comm->cartpmedim = YY;
+ }
+ comm->ntot[comm->cartpmedim]
+ += (cr->npmenodes*dd->nc[comm->cartpmedim])/dd->nnodes;
+ }
+ else if (fplog)
+ {
+ fprintf(fplog,"#pmenodes (%d) is not a multiple of nx*ny (%d*%d) or nx*nz (%d*%d)\n",cr->npmenodes,dd->nc[XX],dd->nc[YY],dd->nc[XX],dd->nc[ZZ]);
+ fprintf(fplog,
+ "Will not use a Cartesian communicator for PP <-> PME\n\n");
+ }
+ }
+
+#ifdef GMX_MPI
+ if (comm->bCartesianPP_PME)
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"Will use a Cartesian communicator for PP <-> PME: %d x %d x %d\n",comm->ntot[XX],comm->ntot[YY],comm->ntot[ZZ]);
+ }
+
+ for(i=0; i<DIM; i++)
+ {
+ periods[i] = TRUE;
+ }
+ MPI_Cart_create(cr->mpi_comm_mysim,DIM,comm->ntot,periods,reorder,
+ &comm_cart);
+
+ MPI_Comm_rank(comm_cart,&rank);
+ if (MASTERNODE(cr) && rank != 0)
+ {
+ gmx_fatal(FARGS,"MPI rank 0 was renumbered by MPI_Cart_create, we do not allow this");
+ }
+
+ /* With this assigment we loose the link to the original communicator
+ * which will usually be MPI_COMM_WORLD, unless have multisim.
+ */
+ cr->mpi_comm_mysim = comm_cart;
+ cr->sim_nodeid = rank;
+
+ MPI_Cart_coords(cr->mpi_comm_mysim,cr->sim_nodeid,DIM,dd->ci);
+
+ if (fplog)
+ {
+ fprintf(fplog,"Cartesian nodeid %d, coordinates %d %d %d\n\n",
+ cr->sim_nodeid,dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
+ }
+
+ if (dd->ci[comm->cartpmedim] < dd->nc[comm->cartpmedim])
+ {
+ cr->duty = DUTY_PP;
+ }
+ if (cr->npmenodes == 0 ||
+ dd->ci[comm->cartpmedim] >= dd->nc[comm->cartpmedim])
+ {
+ cr->duty = DUTY_PME;
+ }
+
+ /* Split the sim communicator into PP and PME only nodes */
+ MPI_Comm_split(cr->mpi_comm_mysim,
+ cr->duty,
+ dd_index(comm->ntot,dd->ci),
+ &cr->mpi_comm_mygroup);
+ }
+ else
+ {
+ switch (dd_node_order)
+ {
+ case ddnoPP_PME:
+ if (fplog)
+ {
+ fprintf(fplog,"Order of the nodes: PP first, PME last\n");
+ }
+ break;
+ case ddnoINTERLEAVE:
+ /* Interleave the PP-only and PME-only nodes,
+ * as on clusters with dual-core machines this will double
+ * the communication bandwidth of the PME processes
+ * and thus speed up the PP <-> PME and inter PME communication.
+ */
+ if (fplog)
+ {
+ fprintf(fplog,"Interleaving PP and PME nodes\n");
+ }
+ comm->pmenodes = dd_pmenodes(cr);
+ break;
+ case ddnoCARTESIAN:
+ break;
+ default:
+ gmx_fatal(FARGS,"Unknown dd_node_order=%d",dd_node_order);
+ }
+
+ if (dd_simnode2pmenode(cr,cr->sim_nodeid) == -1)
+ {
+ cr->duty = DUTY_PME;
+ }
+ else
+ {
+ cr->duty = DUTY_PP;
+ }
+
+ /* Split the sim communicator into PP and PME only nodes */
+ MPI_Comm_split(cr->mpi_comm_mysim,
+ cr->duty,
+ cr->nodeid,
+ &cr->mpi_comm_mygroup);
+ MPI_Comm_rank(cr->mpi_comm_mygroup,&cr->nodeid);
+ }
+#endif
+
+ if (fplog)
+ {
+ fprintf(fplog,"This is a %s only node\n\n",
+ (cr->duty & DUTY_PP) ? "particle-particle" : "PME-mesh");
+ }
+}
+
+void make_dd_communicators(FILE *fplog,t_commrec *cr,int dd_node_order)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ int CartReorder;
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+ copy_ivec(dd->nc,comm->ntot);
+
+ comm->bCartesianPP = (dd_node_order == ddnoCARTESIAN);
+ comm->bCartesianPP_PME = FALSE;
+
+ /* Reorder the nodes by default. This might change the MPI ranks.
+ * Real reordering is only supported on very few architectures,
+ * Blue Gene is one of them.
+ */
+ CartReorder = (getenv("GMX_NO_CART_REORDER") == NULL);
+
+ if (cr->npmenodes > 0)
+ {
+ /* Split the communicator into a PP and PME part */
+ split_communicator(fplog,cr,dd_node_order,CartReorder);
+ if (comm->bCartesianPP_PME)
+ {
+ /* We (possibly) reordered the nodes in split_communicator,
+ * so it is no longer required in make_pp_communicator.
+ */
+ CartReorder = FALSE;
+ }
+ }
+ else
+ {
+ /* All nodes do PP and PME */
+#ifdef GMX_MPI
+ /* We do not require separate communicators */
+ cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
+#endif
+ }
+
+ if (cr->duty & DUTY_PP)
+ {
+ /* Copy or make a new PP communicator */
+ make_pp_communicator(fplog,cr,CartReorder);
+ }
+ else
+ {
+ receive_ddindex2simnodeid(cr);
+ }
+
+ if (!(cr->duty & DUTY_PME))
+ {
+ /* Set up the commnuication to our PME node */
+ dd->pme_nodeid = dd_simnode2pmenode(cr,cr->sim_nodeid);
+ dd->pme_receive_vir_ener = receive_vir_ener(cr);
+ if (debug)
+ {
+ fprintf(debug,"My pme_nodeid %d receive ener %d\n",
+ dd->pme_nodeid,dd->pme_receive_vir_ener);
+ }
+ }
+ else
+ {
+ dd->pme_nodeid = -1;
+ }
+
+ if (DDMASTER(dd))
+ {
+ dd->ma = init_gmx_domdec_master_t(dd,
+ comm->cgs_gl.nr,
+ comm->cgs_gl.index[comm->cgs_gl.nr]);
+ }
+}
+
+static real *get_slb_frac(FILE *fplog,const char *dir,int nc,const char *size_string)
+{
+ real *slb_frac,tot;
+ int i,n;
+ double dbl;
+
+ slb_frac = NULL;
+ if (nc > 1 && size_string != NULL)
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"Using static load balancing for the %s direction\n",
+ dir);
+ }
+ snew(slb_frac,nc);
+ tot = 0;
+ for (i=0; i<nc; i++)
+ {
+ dbl = 0;
+ sscanf(size_string,"%lf%n",&dbl,&n);
+ if (dbl == 0)
+ {
+ gmx_fatal(FARGS,"Incorrect or not enough DD cell size entries for direction %s: '%s'",dir,size_string);
+ }
+ slb_frac[i] = dbl;
+ size_string += n;
+ tot += slb_frac[i];
+ }
+ /* Normalize */
+ if (fplog)
+ {
+ fprintf(fplog,"Relative cell sizes:");
+ }
+ for (i=0; i<nc; i++)
+ {
+ slb_frac[i] /= tot;
+ if (fplog)
+ {
+ fprintf(fplog," %5.3f",slb_frac[i]);
+ }
+ }
+ if (fplog)
+ {
+ fprintf(fplog,"\n");
+ }
+ }
+
+ return slb_frac;
+}
+
+static int multi_body_bondeds_count(gmx_mtop_t *mtop)
+{
+ int n,nmol,ftype;
+ gmx_mtop_ilistloop_t iloop;
+ t_ilist *il;
+
+ n = 0;
+ iloop = gmx_mtop_ilistloop_init(mtop);
+ while (gmx_mtop_ilistloop_next(iloop,&il,&nmol))
+ {
+ for(ftype=0; ftype<F_NRE; ftype++)
+ {
+ if ((interaction_function[ftype].flags & IF_BOND) &&
+ NRAL(ftype) > 2)
+ {
+ n += nmol*il[ftype].nr/(1 + NRAL(ftype));
+ }
+ }
+ }
+
+ return n;
+}
+
+static int dd_nst_env(FILE *fplog,const char *env_var,int def)
+{
+ char *val;
+ int nst;
+
+ nst = def;
+ val = getenv(env_var);
+ if (val)
+ {
+ if (sscanf(val,"%d",&nst) <= 0)
+ {
+ nst = 1;
+ }
+ if (fplog)
+ {
+ fprintf(fplog,"Found env.var. %s = %s, using value %d\n",
+ env_var,val,nst);
+ }
+ }
+
+ return nst;
+}
+
+static void dd_warning(t_commrec *cr,FILE *fplog,const char *warn_string)
+{
+ if (MASTER(cr))
+ {
+ fprintf(stderr,"\n%s\n",warn_string);
+ }
+ if (fplog)
+ {
+ fprintf(fplog,"\n%s\n",warn_string);
+ }
+}
+
+static void check_dd_restrictions(t_commrec *cr,gmx_domdec_t *dd,
+ t_inputrec *ir,FILE *fplog)
+{
+ if (ir->ePBC == epbcSCREW &&
+ (dd->nc[XX] == 1 || dd->nc[YY] > 1 || dd->nc[ZZ] > 1))
+ {
+ gmx_fatal(FARGS,"With pbc=%s can only do domain decomposition in the x-direction",epbc_names[ir->ePBC]);
+ }
+
+ if (ir->ns_type == ensSIMPLE)
+ {
+ gmx_fatal(FARGS,"Domain decomposition does not support simple neighbor searching, use grid searching or use particle decomposition");
+ }
+
+ if (ir->nstlist == 0)
+ {
+ gmx_fatal(FARGS,"Domain decomposition does not work with nstlist=0");
+ }
+
+ if (ir->comm_mode == ecmANGULAR && ir->ePBC != epbcNONE)
+ {
+ dd_warning(cr,fplog,"comm-mode angular will give incorrect results when the comm group partially crosses a periodic boundary");
+ }
+}
+
+static real average_cellsize_min(gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
+{
+ int di,d;
+ real r;
+
+ r = ddbox->box_size[XX];
+ for(di=0; di<dd->ndim; di++)
+ {
+ d = dd->dim[di];
+ /* Check using the initial average cell size */
+ r = min(r,ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
+ }
+
+ return r;
+}
+
+static int check_dlb_support(FILE *fplog,t_commrec *cr,
+ const char *dlb_opt,gmx_bool bRecordLoad,
+ unsigned long Flags,t_inputrec *ir)
+{
+ gmx_domdec_t *dd;
+ int eDLB=-1;
+ char buf[STRLEN];
+
+ switch (dlb_opt[0])
+ {
+ case 'a': eDLB = edlbAUTO; break;
+ case 'n': eDLB = edlbNO; break;
+ case 'y': eDLB = edlbYES; break;
+ default: gmx_incons("Unknown dlb_opt");
+ }
+
+ if (Flags & MD_RERUN)
+ {
+ return edlbNO;
+ }
+
+ if (!EI_DYNAMICS(ir->eI))
+ {
+ if (eDLB == edlbYES)
+ {
+ sprintf(buf,"NOTE: dynamic load balancing is only supported with dynamics, not with integrator '%s'\n",EI(ir->eI));
+ dd_warning(cr,fplog,buf);
+ }
+
+ return edlbNO;
+ }
+
+ if (!bRecordLoad)
+ {
+ dd_warning(cr,fplog,"NOTE: Cycle counting is not supported on this architecture, will not use dynamic load balancing\n");
+
+ return edlbNO;
+ }
+
+ if (Flags & MD_REPRODUCIBLE)
+ {
+ switch (eDLB)
+ {
+ case edlbNO:
+ break;
+ case edlbAUTO:
+ dd_warning(cr,fplog,"NOTE: reproducability requested, will not use dynamic load balancing\n");
+ eDLB = edlbNO;
+ break;
+ case edlbYES:
+ dd_warning(cr,fplog,"WARNING: reproducability requested with dynamic load balancing, the simulation will NOT be binary reproducable\n");
+ break;
+ default:
+ gmx_fatal(FARGS,"Death horror: undefined case (%d) for load balancing choice",eDLB);
+ break;
+ }
+ }
+
+ return eDLB;
+}
+
+static void set_dd_dim(FILE *fplog,gmx_domdec_t *dd)
+{
+ int dim;
+
+ dd->ndim = 0;
+ if (getenv("GMX_DD_ORDER_ZYX") != NULL)
+ {
+ /* Decomposition order z,y,x */
+ if (fplog)
+ {
+ fprintf(fplog,"Using domain decomposition order z, y, x\n");
+ }
+ for(dim=DIM-1; dim>=0; dim--)
+ {
+ if (dd->nc[dim] > 1)
+ {
+ dd->dim[dd->ndim++] = dim;
+ }
+ }
+ }
+ else
+ {
+ /* Decomposition order x,y,z */
+ for(dim=0; dim<DIM; dim++)
+ {
+ if (dd->nc[dim] > 1)
+ {
+ dd->dim[dd->ndim++] = dim;
+ }
+ }
+ }
+}
+
+static gmx_domdec_comm_t *init_dd_comm()
+{
+ gmx_domdec_comm_t *comm;
+ int i;
+
+ snew(comm,1);
+ snew(comm->cggl_flag,DIM*2);
+ snew(comm->cgcm_state,DIM*2);
+ for(i=0; i<DIM*2; i++)
+ {
+ comm->cggl_flag_nalloc[i] = 0;
+ comm->cgcm_state_nalloc[i] = 0;
+ }
+
+ comm->nalloc_int = 0;
+ comm->buf_int = NULL;
+
+ vec_rvec_init(&comm->vbuf);
+
+ comm->n_load_have = 0;
+ comm->n_load_collect = 0;
+
+ for(i=0; i<ddnatNR-ddnatZONE; i++)
+ {
+ comm->sum_nat[i] = 0;
+ }
+ comm->ndecomp = 0;
+ comm->nload = 0;
+ comm->load_step = 0;
+ comm->load_sum = 0;
+ comm->load_max = 0;
+ clear_ivec(comm->load_lim);
+ comm->load_mdf = 0;
+ comm->load_pme = 0;
+
+ return comm;
+}
+
+gmx_domdec_t *init_domain_decomposition(FILE *fplog,t_commrec *cr,
+ unsigned long Flags,
+ ivec nc,
+ real comm_distance_min,real rconstr,
+ const char *dlb_opt,real dlb_scale,
+ const char *sizex,const char *sizey,const char *sizez,
+ gmx_mtop_t *mtop,t_inputrec *ir,
+ matrix box,rvec *x,
+ gmx_ddbox_t *ddbox,
+ int *npme_x,int *npme_y)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ int recload;
+ int d,i,j;
+ real r_2b,r_mb,r_bonded=-1,r_bonded_limit=-1,limit,acs;
+ gmx_bool bC;
+ char buf[STRLEN];
+
+ if (fplog)
+ {
+ fprintf(fplog,
+ "\nInitializing Domain Decomposition on %d nodes\n",cr->nnodes);
+ }
+
+ snew(dd,1);
+
+ dd->comm = init_dd_comm();
+ comm = dd->comm;
+ snew(comm->cggl_flag,DIM*2);
+ snew(comm->cgcm_state,DIM*2);
+
+ dd->npbcdim = ePBC2npbcdim(ir->ePBC);
+ dd->bScrewPBC = (ir->ePBC == epbcSCREW);
+
+ dd->bSendRecv2 = dd_nst_env(fplog,"GMX_DD_SENDRECV2",0);
+ comm->eFlop = dd_nst_env(fplog,"GMX_DLB_FLOP",0);
+ recload = dd_nst_env(fplog,"GMX_DD_LOAD",1);
+ comm->nstSortCG = dd_nst_env(fplog,"GMX_DD_SORT",1);
+ comm->nstDDDump = dd_nst_env(fplog,"GMX_DD_DUMP",0);
+ comm->nstDDDumpGrid = dd_nst_env(fplog,"GMX_DD_DUMP_GRID",0);
+ comm->DD_debug = dd_nst_env(fplog,"GMX_DD_DEBUG",0);
+
+ dd->pme_recv_f_alloc = 0;
+ dd->pme_recv_f_buf = NULL;
+
+ if (dd->bSendRecv2 && fplog)
+ {
+ fprintf(fplog,"Will use two sequential MPI_Sendrecv calls instead of two simultaneous non-blocking MPI_Irecv and MPI_Isend pairs for constraint and vsite communication\n");
+ }
+ if (comm->eFlop)
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"Will load balance based on FLOP count\n");
+ }
+ if (comm->eFlop > 1)
+ {
+ srand(1+cr->nodeid);
+ }
+ comm->bRecordLoad = TRUE;
+ }
+ else
+ {
+ comm->bRecordLoad = (wallcycle_have_counter() && recload > 0);
+
+ }
+
+ comm->eDLB = check_dlb_support(fplog,cr,dlb_opt,comm->bRecordLoad,Flags,ir);
+
+ comm->bDynLoadBal = (comm->eDLB == edlbYES);
+ if (fplog)
+ {
+ fprintf(fplog,"Dynamic load balancing: %s\n",edlb_names[comm->eDLB]);
+ }
+ dd->bGridJump = comm->bDynLoadBal;
+
+ if (comm->nstSortCG)
+ {
+ if (fplog)
+ {
+ if (comm->nstSortCG == 1)
+ {
+ fprintf(fplog,"Will sort the charge groups at every domain (re)decomposition\n");
+ }
+ else
+ {
+ fprintf(fplog,"Will sort the charge groups every %d steps\n",
+ comm->nstSortCG);
+ }
+ }
+ snew(comm->sort,1);
+ }
+ else
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"Will not sort the charge groups\n");
+ }
+ }
+
+ comm->bInterCGBondeds = (ncg_mtop(mtop) > mtop->mols.nr);
+ if (comm->bInterCGBondeds)
+ {
+ comm->bInterCGMultiBody = (multi_body_bondeds_count(mtop) > 0);
+ }
+ else
+ {
+ comm->bInterCGMultiBody = FALSE;
+ }
+
+ dd->bInterCGcons = inter_charge_group_constraints(mtop);
+
+ if (ir->rlistlong == 0)
+ {
+ /* Set the cut-off to some very large value,
+ * so we don't need if statements everywhere in the code.
+ * We use sqrt, since the cut-off is squared in some places.
+ */
+ comm->cutoff = GMX_CUTOFF_INF;
+ }
+ else
+ {
+ comm->cutoff = ir->rlistlong;
+ }
+ comm->cutoff_mbody = 0;
+
+ comm->cellsize_limit = 0;
+ comm->bBondComm = FALSE;
+
+ if (comm->bInterCGBondeds)
+ {
+ if (comm_distance_min > 0)
+ {
+ comm->cutoff_mbody = comm_distance_min;
+ if (Flags & MD_DDBONDCOMM)
+ {
+ comm->bBondComm = (comm->cutoff_mbody > comm->cutoff);
+ }
+ else
+ {
+ comm->cutoff = max(comm->cutoff,comm->cutoff_mbody);
+ }
+ r_bonded_limit = comm->cutoff_mbody;
+ }
+ else if (ir->bPeriodicMols)
+ {
+ /* Can not easily determine the required cut-off */
+ dd_warning(cr,fplog,"NOTE: Periodic molecules: can not easily determine the required minimum bonded cut-off, using half the non-bonded cut-off\n");
+ comm->cutoff_mbody = comm->cutoff/2;
+ r_bonded_limit = comm->cutoff_mbody;
+ }
+ else
+ {
+ if (MASTER(cr))
+ {
+ dd_bonded_cg_distance(fplog,dd,mtop,ir,x,box,
+ Flags & MD_DDBONDCHECK,&r_2b,&r_mb);
+ }
+ gmx_bcast(sizeof(r_2b),&r_2b,cr);
+ gmx_bcast(sizeof(r_mb),&r_mb,cr);
+
+ /* We use an initial margin of 10% for the minimum cell size,
+ * except when we are just below the non-bonded cut-off.
+ */
+ if (Flags & MD_DDBONDCOMM)
+ {
+ if (max(r_2b,r_mb) > comm->cutoff)
+ {
+ r_bonded = max(r_2b,r_mb);
+ r_bonded_limit = 1.1*r_bonded;
+ comm->bBondComm = TRUE;
+ }
+ else
+ {
+ r_bonded = r_mb;
+ r_bonded_limit = min(1.1*r_bonded,comm->cutoff);
+ }
+ /* We determine cutoff_mbody later */
+ }
+ else
+ {
+ /* No special bonded communication,
+ * simply increase the DD cut-off.
+ */
+ r_bonded_limit = 1.1*max(r_2b,r_mb);
+ comm->cutoff_mbody = r_bonded_limit;
+ comm->cutoff = max(comm->cutoff,comm->cutoff_mbody);
+ }
+ }
+ comm->cellsize_limit = max(comm->cellsize_limit,r_bonded_limit);
+ if (fplog)
+ {
+ fprintf(fplog,
+ "Minimum cell size due to bonded interactions: %.3f nm\n",
+ comm->cellsize_limit);
+ }
+ }
+
+ if (dd->bInterCGcons && rconstr <= 0)
+ {
+ /* There is a cell size limit due to the constraints (P-LINCS) */
+ rconstr = constr_r_max(fplog,mtop,ir);
+ if (fplog)
+ {
+ fprintf(fplog,
+ "Estimated maximum distance required for P-LINCS: %.3f nm\n",
+ rconstr);
+ if (rconstr > comm->cellsize_limit)
+ {
+ fprintf(fplog,"This distance will limit the DD cell size, you can override this with -rcon\n");
+ }
+ }
+ }
+ else if (rconstr > 0 && fplog)
+ {
+ /* Here we do not check for dd->bInterCGcons,
+ * because one can also set a cell size limit for virtual sites only
+ * and at this point we don't know yet if there are intercg v-sites.
+ */
+ fprintf(fplog,
+ "User supplied maximum distance required for P-LINCS: %.3f nm\n",
+ rconstr);
+ }
+ comm->cellsize_limit = max(comm->cellsize_limit,rconstr);
+
+ comm->cgs_gl = gmx_mtop_global_cgs(mtop);
+
+ if (nc[XX] > 0)
+ {
+ copy_ivec(nc,dd->nc);
+ set_dd_dim(fplog,dd);
+ set_ddbox_cr(cr,&dd->nc,ir,box,&comm->cgs_gl,x,ddbox);
+
+ if (cr->npmenodes == -1)
+ {
+ cr->npmenodes = 0;
+ }
+ acs = average_cellsize_min(dd,ddbox);
+ if (acs < comm->cellsize_limit)
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"ERROR: The initial cell size (%f) is smaller than the cell size limit (%f)\n",acs,comm->cellsize_limit);
+ }
+ gmx_fatal_collective(FARGS,cr,NULL,
+ "The initial cell size (%f) is smaller than the cell size limit (%f), change options -dd, -rdd or -rcon, see the log file for details",
+ acs,comm->cellsize_limit);
+ }
+ }
+ else
+ {
+ set_ddbox_cr(cr,NULL,ir,box,&comm->cgs_gl,x,ddbox);
+
+ /* We need to choose the optimal DD grid and possibly PME nodes */
+ limit = dd_choose_grid(fplog,cr,dd,ir,mtop,box,ddbox,
+ comm->eDLB!=edlbNO,dlb_scale,
+ comm->cellsize_limit,comm->cutoff,
+ comm->bInterCGBondeds,comm->bInterCGMultiBody);
+
+ if (dd->nc[XX] == 0)
+ {
+ bC = (dd->bInterCGcons && rconstr > r_bonded_limit);
+ sprintf(buf,"Change the number of nodes or mdrun option %s%s%s",
+ !bC ? "-rdd" : "-rcon",
+ comm->eDLB!=edlbNO ? " or -dds" : "",
+ bC ? " or your LINCS settings" : "");
+
+ gmx_fatal_collective(FARGS,cr,NULL,
+ "There is no domain decomposition for %d nodes that is compatible with the given box and a minimum cell size of %g nm\n"
+ "%s\n"
+ "Look in the log file for details on the domain decomposition",
+ cr->nnodes-cr->npmenodes,limit,buf);
+ }
+ set_dd_dim(fplog,dd);
+ }
+
+ if (fplog)
+ {
+ fprintf(fplog,
+ "Domain decomposition grid %d x %d x %d, separate PME nodes %d\n",
+ dd->nc[XX],dd->nc[YY],dd->nc[ZZ],cr->npmenodes);
+ }
+
+ dd->nnodes = dd->nc[XX]*dd->nc[YY]*dd->nc[ZZ];
+ if (cr->nnodes - dd->nnodes != cr->npmenodes)
+ {
+ gmx_fatal_collective(FARGS,cr,NULL,
+ "The size of the domain decomposition grid (%d) does not match the number of nodes (%d). The total number of nodes is %d",
+ dd->nnodes,cr->nnodes - cr->npmenodes,cr->nnodes);
+ }
+ if (cr->npmenodes > dd->nnodes)
+ {
+ gmx_fatal_collective(FARGS,cr,NULL,
+ "The number of separate PME node (%d) is larger than the number of PP nodes (%d), this is not supported.",cr->npmenodes,dd->nnodes);
+ }
+ if (cr->npmenodes > 0)
+ {
+ comm->npmenodes = cr->npmenodes;
+ }
+ else
+ {
+ comm->npmenodes = dd->nnodes;
+ }
+
+ if (EEL_PME(ir->coulombtype))
+ {
+ /* The following choices should match those
+ * in comm_cost_est in domdec_setup.c.
+ * Note that here the checks have to take into account
+ * that the decomposition might occur in a different order than xyz
+ * (for instance through the env.var. GMX_DD_ORDER_ZYX),
+ * in which case they will not match those in comm_cost_est,
+ * but since that is mainly for testing purposes that's fine.
+ */
+ if (dd->ndim >= 2 && dd->dim[0] == XX && dd->dim[1] == YY &&
+ comm->npmenodes > dd->nc[XX] && comm->npmenodes % dd->nc[XX] == 0 &&
+ getenv("GMX_PMEONEDD") == NULL)
+ {
+ comm->npmedecompdim = 2;
+ comm->npmenodes_x = dd->nc[XX];
+ comm->npmenodes_y = comm->npmenodes/comm->npmenodes_x;
+ }
+ else
+ {
+ /* In case nc is 1 in both x and y we could still choose to
+ * decompose pme in y instead of x, but we use x for simplicity.
+ */
+ comm->npmedecompdim = 1;
+ if (dd->dim[0] == YY)
+ {
+ comm->npmenodes_x = 1;
+ comm->npmenodes_y = comm->npmenodes;
+ }
+ else
+ {
+ comm->npmenodes_x = comm->npmenodes;
+ comm->npmenodes_y = 1;
+ }
+ }
+ if (fplog)
+ {
+ fprintf(fplog,"PME domain decomposition: %d x %d x %d\n",
+ comm->npmenodes_x,comm->npmenodes_y,1);
+ }
+ }
+ else
+ {
+ comm->npmedecompdim = 0;
+ comm->npmenodes_x = 0;
+ comm->npmenodes_y = 0;
+ }
+
+ /* Technically we don't need both of these,
+ * but it simplifies code not having to recalculate it.
+ */
+ *npme_x = comm->npmenodes_x;
+ *npme_y = comm->npmenodes_y;
+
+ snew(comm->slb_frac,DIM);
+ if (comm->eDLB == edlbNO)
+ {
+ comm->slb_frac[XX] = get_slb_frac(fplog,"x",dd->nc[XX],sizex);
+ comm->slb_frac[YY] = get_slb_frac(fplog,"y",dd->nc[YY],sizey);
+ comm->slb_frac[ZZ] = get_slb_frac(fplog,"z",dd->nc[ZZ],sizez);
+ }
+
+ if (comm->bInterCGBondeds && comm->cutoff_mbody == 0)
+ {
+ if (comm->bBondComm || comm->eDLB != edlbNO)
+ {
+ /* Set the bonded communication distance to halfway
+ * the minimum and the maximum,
+ * since the extra communication cost is nearly zero.
+ */
+ acs = average_cellsize_min(dd,ddbox);
+ comm->cutoff_mbody = 0.5*(r_bonded + acs);
+ if (comm->eDLB != edlbNO)
+ {
+ /* Check if this does not limit the scaling */
+ comm->cutoff_mbody = min(comm->cutoff_mbody,dlb_scale*acs);
+ }
+ if (!comm->bBondComm)
+ {
+ /* Without bBondComm do not go beyond the n.b. cut-off */
+ comm->cutoff_mbody = min(comm->cutoff_mbody,comm->cutoff);
+ if (comm->cellsize_limit >= comm->cutoff)
+ {
+ /* We don't loose a lot of efficieny
+ * when increasing it to the n.b. cut-off.
+ * It can even be slightly faster, because we need
+ * less checks for the communication setup.
+ */
+ comm->cutoff_mbody = comm->cutoff;
+ }
+ }
+ /* Check if we did not end up below our original limit */
+ comm->cutoff_mbody = max(comm->cutoff_mbody,r_bonded_limit);
+
+ if (comm->cutoff_mbody > comm->cellsize_limit)
+ {
+ comm->cellsize_limit = comm->cutoff_mbody;
+ }
+ }
+ /* Without DLB and cutoff_mbody<cutoff, cutoff_mbody is dynamic */
+ }
+
+ if (debug)
+ {
+ fprintf(debug,"Bonded atom communication beyond the cut-off: %d\n"
+ "cellsize limit %f\n",
+ comm->bBondComm,comm->cellsize_limit);
+ }
+
+ if (MASTER(cr))
+ {
+ check_dd_restrictions(cr,dd,ir,fplog);
+ }
+
+ comm->partition_step = INT_MIN;
+ dd->ddp_count = 0;
+
+ clear_dd_cycle_counts(dd);
+
+ return dd;
+}
+
+static void set_dlb_limits(gmx_domdec_t *dd)
+
+{
+ int d;
+
+ for(d=0; d<dd->ndim; d++)
+ {
+ dd->comm->cd[d].np = dd->comm->cd[d].np_dlb;
+ dd->comm->cellsize_min[dd->dim[d]] =
+ dd->comm->cellsize_min_dlb[dd->dim[d]];
+ }
+}
+
+
+static void turn_on_dlb(FILE *fplog,t_commrec *cr,gmx_large_int_t step)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ real cellsize_min;
+ int d,nc,i;
+ char buf[STRLEN];
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+ if (fplog)
+ {
+ fprintf(fplog,"At step %s the performance loss due to force load imbalance is %.1f %%\n",gmx_step_str(step,buf),dd_force_imb_perf_loss(dd)*100);
+ }
+
+ cellsize_min = comm->cellsize_min[dd->dim[0]];
+ for(d=1; d<dd->ndim; d++)
+ {
+ cellsize_min = min(cellsize_min,comm->cellsize_min[dd->dim[d]]);
+ }
+
+ if (cellsize_min < comm->cellsize_limit*1.05)
+ {
+ dd_warning(cr,fplog,"NOTE: the minimum cell size is smaller than 1.05 times the cell size limit, will not turn on dynamic load balancing\n");
+
+ /* Change DLB from "auto" to "no". */
+ comm->eDLB = edlbNO;
+
+ return;
+ }
+
+ dd_warning(cr,fplog,"NOTE: Turning on dynamic load balancing\n");
+ comm->bDynLoadBal = TRUE;
+ dd->bGridJump = TRUE;
+
+ set_dlb_limits(dd);
+
+ /* We can set the required cell size info here,
+ * so we do not need to communicate this.
+ * The grid is completely uniform.
+ */
+ for(d=0; d<dd->ndim; d++)
+ {
+ if (comm->root[d])
+ {
+ comm->load[d].sum_m = comm->load[d].sum;
+
+ nc = dd->nc[dd->dim[d]];
+ for(i=0; i<nc; i++)
+ {
+ comm->root[d]->cell_f[i] = i/(real)nc;
+ if (d > 0)
+ {
+ comm->root[d]->cell_f_max0[i] = i /(real)nc;
+ comm->root[d]->cell_f_min1[i] = (i+1)/(real)nc;
+ }
+ }
+ comm->root[d]->cell_f[nc] = 1.0;
+ }
+ }
+}
+
+static char *init_bLocalCG(gmx_mtop_t *mtop)
+{
+ int ncg,cg;
+ char *bLocalCG;
+
+ ncg = ncg_mtop(mtop);
+ snew(bLocalCG,ncg);
+ for(cg=0; cg<ncg; cg++)
+ {
+ bLocalCG[cg] = FALSE;
+ }
+
+ return bLocalCG;
+}
+
+void dd_init_bondeds(FILE *fplog,
+ gmx_domdec_t *dd,gmx_mtop_t *mtop,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ t_inputrec *ir,gmx_bool bBCheck,cginfo_mb_t *cginfo_mb)
+{
+ gmx_domdec_comm_t *comm;
+ gmx_bool bBondComm;
+ int d;
+
+ dd_make_reverse_top(fplog,dd,mtop,vsite,constr,ir,bBCheck);
+
+ comm = dd->comm;
+
+ if (comm->bBondComm)
+ {
+ /* Communicate atoms beyond the cut-off for bonded interactions */
+ comm = dd->comm;
+
+ comm->cglink = make_charge_group_links(mtop,dd,cginfo_mb);
+
+ comm->bLocalCG = init_bLocalCG(mtop);
+ }
+ else
+ {
+ /* Only communicate atoms based on cut-off */
+ comm->cglink = NULL;
+ comm->bLocalCG = NULL;
+ }
+}
+
+static void print_dd_settings(FILE *fplog,gmx_domdec_t *dd,
+ t_inputrec *ir,
+ gmx_bool bDynLoadBal,real dlb_scale,
+ gmx_ddbox_t *ddbox)
+{
+ gmx_domdec_comm_t *comm;
+ int d;
+ ivec np;
+ real limit,shrink;
+ char buf[64];
+
+ if (fplog == NULL)
+ {
+ return;
+ }
+
+ comm = dd->comm;
+
+ if (bDynLoadBal)
+ {
+ fprintf(fplog,"The maximum number of communication pulses is:");
+ for(d=0; d<dd->ndim; d++)
+ {
+ fprintf(fplog," %c %d",dim2char(dd->dim[d]),comm->cd[d].np_dlb);
+ }
+ fprintf(fplog,"\n");
+ fprintf(fplog,"The minimum size for domain decomposition cells is %.3f nm\n",comm->cellsize_limit);
+ fprintf(fplog,"The requested allowed shrink of DD cells (option -dds) is: %.2f\n",dlb_scale);
+ fprintf(fplog,"The allowed shrink of domain decomposition cells is:");
+ for(d=0; d<DIM; d++)
+ {
+ if (dd->nc[d] > 1)
+ {
+ if (d >= ddbox->npbcdim && dd->nc[d] == 2)
+ {
+ shrink = 0;
+ }
+ else
+ {
+ shrink =
+ comm->cellsize_min_dlb[d]/
+ (ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
+ }
+ fprintf(fplog," %c %.2f",dim2char(d),shrink);
+ }
+ }
+ fprintf(fplog,"\n");
+ }
+ else
+ {
+ set_dd_cell_sizes_slb(dd,ddbox,FALSE,np);
+ fprintf(fplog,"The initial number of communication pulses is:");
+ for(d=0; d<dd->ndim; d++)
+ {
+ fprintf(fplog," %c %d",dim2char(dd->dim[d]),np[dd->dim[d]]);
+ }
+ fprintf(fplog,"\n");
+ fprintf(fplog,"The initial domain decomposition cell size is:");
+ for(d=0; d<DIM; d++) {
+ if (dd->nc[d] > 1)
+ {
+ fprintf(fplog," %c %.2f nm",
+ dim2char(d),dd->comm->cellsize_min[d]);
+ }
+ }
+ fprintf(fplog,"\n\n");
+ }
+
+ if (comm->bInterCGBondeds || dd->vsite_comm || dd->constraint_comm)
+ {
+ fprintf(fplog,"The maximum allowed distance for charge groups involved in interactions is:\n");
+ fprintf(fplog,"%40s %-7s %6.3f nm\n",
+ "non-bonded interactions","",comm->cutoff);
+
+ if (bDynLoadBal)
+ {
+ limit = dd->comm->cellsize_limit;
+ }
+ else
+ {
+ if (dynamic_dd_box(ddbox,ir))
+ {
+ fprintf(fplog,"(the following are initial values, they could change due to box deformation)\n");
+ }
+ limit = dd->comm->cellsize_min[XX];
+ for(d=1; d<DIM; d++)
+ {
+ limit = min(limit,dd->comm->cellsize_min[d]);
+ }
+ }
+
+ if (comm->bInterCGBondeds)
+ {
+ fprintf(fplog,"%40s %-7s %6.3f nm\n",
+ "two-body bonded interactions","(-rdd)",
+ max(comm->cutoff,comm->cutoff_mbody));
+ fprintf(fplog,"%40s %-7s %6.3f nm\n",
+ "multi-body bonded interactions","(-rdd)",
+ (comm->bBondComm || dd->bGridJump) ? comm->cutoff_mbody : min(comm->cutoff,limit));
+ }
+ if (dd->vsite_comm)
+ {
+ fprintf(fplog,"%40s %-7s %6.3f nm\n",
+ "virtual site constructions","(-rcon)",limit);
+ }
+ if (dd->constraint_comm)
+ {
+ sprintf(buf,"atoms separated by up to %d constraints",
+ 1+ir->nProjOrder);
+ fprintf(fplog,"%40s %-7s %6.3f nm\n",
+ buf,"(-rcon)",limit);
+ }
+ fprintf(fplog,"\n");
+ }
+
+ fflush(fplog);
+}
+
+void set_dd_parameters(FILE *fplog,gmx_domdec_t *dd,real dlb_scale,
+ t_inputrec *ir,t_forcerec *fr,
+ gmx_ddbox_t *ddbox)
+{
+ gmx_domdec_comm_t *comm;
+ int d,dim,npulse,npulse_d_max,npulse_d;
+ gmx_bool bNoCutOff;
+ int natoms_tot;
+ real vol_frac;
+
+ comm = dd->comm;
+
+ bNoCutOff = (ir->rvdw == 0 || ir->rcoulomb == 0);
+
+ if (EEL_PME(ir->coulombtype))
+ {
+ init_ddpme(dd,&comm->ddpme[0],0);
+ if (comm->npmedecompdim >= 2)
+ {
+ init_ddpme(dd,&comm->ddpme[1],1);
+ }
+ }
+ else
+ {
+ comm->npmenodes = 0;
+ if (dd->pme_nodeid >= 0)
+ {
+ gmx_fatal_collective(FARGS,NULL,dd,
+ "Can not have separate PME nodes without PME electrostatics");
+ }
+ }
+
+ /* If each molecule is a single charge group
+ * or we use domain decomposition for each periodic dimension,
+ * we do not need to take pbc into account for the bonded interactions.
+ */
+ if (fr->ePBC == epbcNONE || !comm->bInterCGBondeds ||
+ (dd->nc[XX]>1 && dd->nc[YY]>1 && (dd->nc[ZZ]>1 || fr->ePBC==epbcXY)))
+ {
+ fr->bMolPBC = FALSE;
+ }
+ else
+ {
+ fr->bMolPBC = TRUE;
+ }
+
+ if (debug)
+ {
+ fprintf(debug,"The DD cut-off is %f\n",comm->cutoff);
+ }
+ if (comm->eDLB != edlbNO)
+ {
+ /* Determine the maximum number of comm. pulses in one dimension */
+
+ comm->cellsize_limit = max(comm->cellsize_limit,comm->cutoff_mbody);
+
+ /* Determine the maximum required number of grid pulses */
+ if (comm->cellsize_limit >= comm->cutoff)
+ {
+ /* Only a single pulse is required */
+ npulse = 1;
+ }
+ else if (!bNoCutOff && comm->cellsize_limit > 0)
+ {
+ /* We round down slightly here to avoid overhead due to the latency
+ * of extra communication calls when the cut-off
+ * would be only slightly longer than the cell size.
+ * Later cellsize_limit is redetermined,
+ * so we can not miss interactions due to this rounding.
+ */
+ npulse = (int)(0.96 + comm->cutoff/comm->cellsize_limit);
+ }
+ else
+ {
+ /* There is no cell size limit */
+ npulse = max(dd->nc[XX]-1,max(dd->nc[YY]-1,dd->nc[ZZ]-1));
+ }
+
+ if (!bNoCutOff && npulse > 1)
+ {
+ /* See if we can do with less pulses, based on dlb_scale */
+ npulse_d_max = 0;
+ for(d=0; d<dd->ndim; d++)
+ {
+ dim = dd->dim[d];
+ npulse_d = (int)(1 + dd->nc[dim]*comm->cutoff
+ /(ddbox->box_size[dim]*ddbox->skew_fac[dim]*dlb_scale));
+ npulse_d_max = max(npulse_d_max,npulse_d);
+ }
+ npulse = min(npulse,npulse_d_max);
+ }
+
+ /* This env var can override npulse */
+ d = dd_nst_env(fplog,"GMX_DD_NPULSE",0);
+ if (d > 0)
+ {
+ npulse = d;
+ }
+
+ comm->maxpulse = 1;
+ comm->bVacDLBNoLimit = (ir->ePBC == epbcNONE);
+ for(d=0; d<dd->ndim; d++)
+ {
+ comm->cd[d].np_dlb = min(npulse,dd->nc[dd->dim[d]]-1);
+ comm->cd[d].np_nalloc = comm->cd[d].np_dlb;
+ snew(comm->cd[d].ind,comm->cd[d].np_nalloc);
+ comm->maxpulse = max(comm->maxpulse,comm->cd[d].np_dlb);
+ if (comm->cd[d].np_dlb < dd->nc[dd->dim[d]]-1)
+ {
+ comm->bVacDLBNoLimit = FALSE;
+ }
+ }
+
+ /* cellsize_limit is set for LINCS in init_domain_decomposition */
+ if (!comm->bVacDLBNoLimit)
+ {
+ comm->cellsize_limit = max(comm->cellsize_limit,
+ comm->cutoff/comm->maxpulse);
+ }
+ comm->cellsize_limit = max(comm->cellsize_limit,comm->cutoff_mbody);
+ /* Set the minimum cell size for each DD dimension */
+ for(d=0; d<dd->ndim; d++)
+ {
+ if (comm->bVacDLBNoLimit ||
+ comm->cd[d].np_dlb*comm->cellsize_limit >= comm->cutoff)
+ {
+ comm->cellsize_min_dlb[dd->dim[d]] = comm->cellsize_limit;
+ }
+ else
+ {
+ comm->cellsize_min_dlb[dd->dim[d]] =
+ comm->cutoff/comm->cd[d].np_dlb;
+ }
+ }
+ if (comm->cutoff_mbody <= 0)
+ {
+ comm->cutoff_mbody = min(comm->cutoff,comm->cellsize_limit);
+ }
+ if (comm->bDynLoadBal)
+ {
+ set_dlb_limits(dd);
+ }
+ }
+
+ print_dd_settings(fplog,dd,ir,comm->bDynLoadBal,dlb_scale,ddbox);
+ if (comm->eDLB == edlbAUTO)
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"When dynamic load balancing gets turned on, these settings will change to:\n");
+ }
+ print_dd_settings(fplog,dd,ir,TRUE,dlb_scale,ddbox);
+ }
+
+ if (ir->ePBC == epbcNONE)
+ {
+ vol_frac = 1 - 1/(double)dd->nnodes;
+ }
+ else
+ {
+ vol_frac =
+ (1 + comm_box_frac(dd->nc,comm->cutoff,ddbox))/(double)dd->nnodes;
+ }
+ if (debug)
+ {
+ fprintf(debug,"Volume fraction for all DD zones: %f\n",vol_frac);
+ }
+ natoms_tot = comm->cgs_gl.index[comm->cgs_gl.nr];
+
+ dd->ga2la = ga2la_init(natoms_tot,vol_frac*natoms_tot);
+}
+
+static void merge_cg_buffers(int ncell,
+ gmx_domdec_comm_dim_t *cd, int pulse,
+ int *ncg_cell,
+ int *index_gl, int *recv_i,
+ rvec *cg_cm, rvec *recv_vr,
+ int *cgindex,
+ cginfo_mb_t *cginfo_mb,int *cginfo)
+{
+ gmx_domdec_ind_t *ind,*ind_p;
+ int p,cell,c,cg,cg0,cg1,cg_gl,nat;
+ int shift,shift_at;
+
+ ind = &cd->ind[pulse];
+
+ /* First correct the already stored data */
+ shift = ind->nrecv[ncell];
+ for(cell=ncell-1; cell>=0; cell--)
+ {
+ shift -= ind->nrecv[cell];
+ if (shift > 0)
+ {
+ /* Move the cg's present from previous grid pulses */
+ cg0 = ncg_cell[ncell+cell];
+ cg1 = ncg_cell[ncell+cell+1];
+ cgindex[cg1+shift] = cgindex[cg1];
+ for(cg=cg1-1; cg>=cg0; cg--)
+ {
+ index_gl[cg+shift] = index_gl[cg];
+ copy_rvec(cg_cm[cg],cg_cm[cg+shift]);
+ cgindex[cg+shift] = cgindex[cg];
+ cginfo[cg+shift] = cginfo[cg];
+ }
+ /* Correct the already stored send indices for the shift */
+ for(p=1; p<=pulse; p++)
+ {
+ ind_p = &cd->ind[p];
+ cg0 = 0;
+ for(c=0; c<cell; c++)
+ {
+ cg0 += ind_p->nsend[c];
+ }
+ cg1 = cg0 + ind_p->nsend[cell];
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ ind_p->index[cg] += shift;
+ }
+ }
+ }
+ }
+
+ /* Merge in the communicated buffers */
+ shift = 0;
+ shift_at = 0;
+ cg0 = 0;
+ for(cell=0; cell<ncell; cell++)
+ {
+ cg1 = ncg_cell[ncell+cell+1] + shift;
+ if (shift_at > 0)
+ {
+ /* Correct the old cg indices */
+ for(cg=ncg_cell[ncell+cell]; cg<cg1; cg++)
+ {
+ cgindex[cg+1] += shift_at;
+ }
+ }
+ for(cg=0; cg<ind->nrecv[cell]; cg++)
+ {
+ /* Copy this charge group from the buffer */
+ index_gl[cg1] = recv_i[cg0];
+ copy_rvec(recv_vr[cg0],cg_cm[cg1]);
+ /* Add it to the cgindex */
+ cg_gl = index_gl[cg1];
+ cginfo[cg1] = ddcginfo(cginfo_mb,cg_gl);
+ nat = GET_CGINFO_NATOMS(cginfo[cg1]);
+ cgindex[cg1+1] = cgindex[cg1] + nat;
+ cg0++;
+ cg1++;
+ shift_at += nat;
+ }
+ shift += ind->nrecv[cell];
+ ncg_cell[ncell+cell+1] = cg1;
+ }
+}
+
+static void make_cell2at_index(gmx_domdec_comm_dim_t *cd,
+ int nzone,int cg0,const int *cgindex)
+{
+ int cg,zone,p;
+
+ /* Store the atom block boundaries for easy copying of communication buffers
+ */
+ cg = cg0;
+ for(zone=0; zone<nzone; zone++)
+ {
+ for(p=0; p<cd->np; p++) {
+ cd->ind[p].cell2at0[zone] = cgindex[cg];
+ cg += cd->ind[p].nrecv[zone];
+ cd->ind[p].cell2at1[zone] = cgindex[cg];
+ }
+ }
+}
+
+static gmx_bool missing_link(t_blocka *link,int cg_gl,char *bLocalCG)
+{
+ int i;
+ gmx_bool bMiss;
+
+ bMiss = FALSE;
+ for(i=link->index[cg_gl]; i<link->index[cg_gl+1]; i++)
+ {
+ if (!bLocalCG[link->a[i]])
+ {
+ bMiss = TRUE;
+ }
+ }
+
+ return bMiss;
+}
+
+static void setup_dd_communication(gmx_domdec_t *dd,
+ matrix box,gmx_ddbox_t *ddbox,t_forcerec *fr)
+{
+ int dim_ind,dim,dim0,dim1=-1,dim2=-1,dimd,p,nat_tot;
+ int nzone,nzone_send,zone,zonei,cg0,cg1;
+ int c,i,j,cg,cg_gl,nrcg;
+ int *zone_cg_range,pos_cg,*index_gl,*cgindex,*recv_i;
+ gmx_domdec_comm_t *comm;
+ gmx_domdec_zones_t *zones;
+ gmx_domdec_comm_dim_t *cd;
+ gmx_domdec_ind_t *ind;
+ cginfo_mb_t *cginfo_mb;
+ gmx_bool bBondComm,bDist2B,bDistMB,bDistMB_pulse,bDistBonded,bScrew;
+ real r_mb,r_comm2,r_scomm2,r_bcomm2,r,r_0,r_1,r2,rb2,r2inc,inv_ncg,tric_sh;
+ rvec rb,rn;
+ real corner[DIM][4],corner_round_0=0,corner_round_1[4];
+ real bcorner[DIM],bcorner_round_1=0;
+ ivec tric_dist;
+ rvec *cg_cm,*normal,*v_d,*v_0=NULL,*v_1=NULL,*recv_vr;
+ real skew_fac2_d,skew_fac_01;
+ rvec sf2_round;
+ int nsend,nat;
+
+ if (debug)
+ {
+ fprintf(debug,"Setting up DD communication\n");
+ }
+
+ comm = dd->comm;
+ cg_cm = fr->cg_cm;
+
+ for(dim_ind=0; dim_ind<dd->ndim; dim_ind++)
+ {
+ dim = dd->dim[dim_ind];
+
+ /* Check if we need to use triclinic distances */
+ tric_dist[dim_ind] = 0;
+ for(i=0; i<=dim_ind; i++)
+ {
+ if (ddbox->tric_dir[dd->dim[i]])
+ {
+ tric_dist[dim_ind] = 1;
+ }
+ }
+ }
+
+ bBondComm = comm->bBondComm;
+
+ /* Do we need to determine extra distances for multi-body bondeds? */
+ bDistMB = (comm->bInterCGMultiBody && dd->bGridJump && dd->ndim > 1);
+
+ /* Do we need to determine extra distances for only two-body bondeds? */
+ bDist2B = (bBondComm && !bDistMB);
+
+ r_comm2 = sqr(comm->cutoff);
+ r_bcomm2 = sqr(comm->cutoff_mbody);
+
+ if (debug)
+ {
+ fprintf(debug,"bBondComm %d, r_bc %f\n",bBondComm,sqrt(r_bcomm2));
+ }
+
+ zones = &comm->zones;
+
+ dim0 = dd->dim[0];
+ /* The first dimension is equal for all cells */
+ corner[0][0] = comm->cell_x0[dim0];
+ if (bDistMB)
+ {
+ bcorner[0] = corner[0][0];
+ }
+ if (dd->ndim >= 2)
+ {
+ dim1 = dd->dim[1];
+ /* This cell row is only seen from the first row */
+ corner[1][0] = comm->cell_x0[dim1];
+ /* All rows can see this row */
+ corner[1][1] = comm->cell_x0[dim1];
+ if (dd->bGridJump)
+ {
+ corner[1][1] = max(comm->cell_x0[dim1],comm->zone_d1[1].mch0);
+ if (bDistMB)
+ {
+ /* For the multi-body distance we need the maximum */
+ bcorner[1] = max(comm->cell_x0[dim1],comm->zone_d1[1].p1_0);
+ }
+ }
+ /* Set the upper-right corner for rounding */
+ corner_round_0 = comm->cell_x1[dim0];
+
+ if (dd->ndim >= 3)
+ {
+ dim2 = dd->dim[2];
+ for(j=0; j<4; j++)
+ {
+ corner[2][j] = comm->cell_x0[dim2];
+ }
+ if (dd->bGridJump)
+ {
+ /* Use the maximum of the i-cells that see a j-cell */
+ for(i=0; i<zones->nizone; i++)
+ {
+ for(j=zones->izone[i].j0; j<zones->izone[i].j1; j++)
+ {
+ if (j >= 4)
+ {
+ corner[2][j-4] =
+ max(corner[2][j-4],
+ comm->zone_d2[zones->shift[i][dim0]][zones->shift[i][dim1]].mch0);
+ }
+ }
+ }
+ if (bDistMB)
+ {
+ /* For the multi-body distance we need the maximum */
+ bcorner[2] = comm->cell_x0[dim2];
+ for(i=0; i<2; i++)
+ {
+ for(j=0; j<2; j++)
+ {
+ bcorner[2] = max(bcorner[2],
+ comm->zone_d2[i][j].p1_0);
+ }
+ }
+ }
+ }
+
+ /* Set the upper-right corner for rounding */
+ /* Cell (0,0,0) and cell (1,0,0) can see cell 4 (0,1,1)
+ * Only cell (0,0,0) can see cell 7 (1,1,1)
+ */
+ corner_round_1[0] = comm->cell_x1[dim1];
+ corner_round_1[3] = comm->cell_x1[dim1];
+ if (dd->bGridJump)
+ {
+ corner_round_1[0] = max(comm->cell_x1[dim1],
+ comm->zone_d1[1].mch1);
+ if (bDistMB)
+ {
+ /* For the multi-body distance we need the maximum */
+ bcorner_round_1 = max(comm->cell_x1[dim1],
+ comm->zone_d1[1].p1_1);
+ }
+ }
+ }
+ }
+
+ /* Triclinic stuff */
+ normal = ddbox->normal;
+ skew_fac_01 = 0;
+ if (dd->ndim >= 2)
+ {
+ v_0 = ddbox->v[dim0];
+ if (ddbox->tric_dir[dim0] && ddbox->tric_dir[dim1])
+ {
+ /* Determine the coupling coefficient for the distances
+ * to the cell planes along dim0 and dim1 through dim2.
+ * This is required for correct rounding.
+ */
+ skew_fac_01 =
+ ddbox->v[dim0][dim1+1][dim0]*ddbox->v[dim1][dim1+1][dim1];
+ if (debug)
+ {
+ fprintf(debug,"\nskew_fac_01 %f\n",skew_fac_01);
+ }
+ }
+ }
+ if (dd->ndim >= 3)
+ {
+ v_1 = ddbox->v[dim1];
+ }
+
+ zone_cg_range = zones->cg_range;
+ index_gl = dd->index_gl;
+ cgindex = dd->cgindex;
+ cginfo_mb = fr->cginfo_mb;
+
+ zone_cg_range[0] = 0;
+ zone_cg_range[1] = dd->ncg_home;
+ comm->zone_ncg1[0] = dd->ncg_home;
+ pos_cg = dd->ncg_home;
+
+ nat_tot = dd->nat_home;
+ nzone = 1;
+ for(dim_ind=0; dim_ind<dd->ndim; dim_ind++)
+ {
+ dim = dd->dim[dim_ind];
+ cd = &comm->cd[dim_ind];
+
+ if (dim >= ddbox->npbcdim && dd->ci[dim] == 0)
+ {
+ /* No pbc in this dimension, the first node should not comm. */
+ nzone_send = 0;
+ }
+ else
+ {
+ nzone_send = nzone;
+ }
+
+ bScrew = (dd->bScrewPBC && dim == XX);
+
+ v_d = ddbox->v[dim];
+ skew_fac2_d = sqr(ddbox->skew_fac[dim]);
+
+ cd->bInPlace = TRUE;
+ for(p=0; p<cd->np; p++)
+ {
+ /* Only atoms communicated in the first pulse are used
+ * for multi-body bonded interactions or for bBondComm.
+ */
+ bDistBonded = ((bDistMB || bDist2B) && p == 0);
+ bDistMB_pulse = (bDistMB && bDistBonded);
+
+ ind = &cd->ind[p];
+ nsend = 0;
+ nat = 0;
+ for(zone=0; zone<nzone_send; zone++)
+ {
+ if (tric_dist[dim_ind] && dim_ind > 0)
+ {
+ /* Determine slightly more optimized skew_fac's
+ * for rounding.
+ * This reduces the number of communicated atoms
+ * by about 10% for 3D DD of rhombic dodecahedra.
+ */
+ for(dimd=0; dimd<dim; dimd++)
+ {
+ sf2_round[dimd] = 1;
+ if (ddbox->tric_dir[dimd])
+ {
+ for(i=dd->dim[dimd]+1; i<DIM; i++)
+ {
+ /* If we are shifted in dimension i
+ * and the cell plane is tilted forward
+ * in dimension i, skip this coupling.
+ */
+ if (!(zones->shift[nzone+zone][i] &&
+ ddbox->v[dimd][i][dimd] >= 0))
+ {
+ sf2_round[dimd] +=
+ sqr(ddbox->v[dimd][i][dimd]);
+ }
+ }
+ sf2_round[dimd] = 1/sf2_round[dimd];
+ }
+ }
+ }
+
+ zonei = zone_perm[dim_ind][zone];
+ if (p == 0)
+ {
+ /* Here we permutate the zones to obtain a convenient order
+ * for neighbor searching
+ */
+ cg0 = zone_cg_range[zonei];
+ cg1 = zone_cg_range[zonei+1];
+ }
+ else
+ {
+ /* Look only at the cg's received in the previous grid pulse
+ */
+ cg1 = zone_cg_range[nzone+zone+1];
+ cg0 = cg1 - cd->ind[p-1].nrecv[zone];
+ }
+ ind->nsend[zone] = 0;
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ r2 = 0;
+ rb2 = 0;
+ if (tric_dist[dim_ind] == 0)
+ {
+ /* Rectangular direction, easy */
+ r = cg_cm[cg][dim] - corner[dim_ind][zone];
+ if (r > 0)
+ {
+ r2 += r*r;
+ }
+ if (bDistMB_pulse)
+ {
+ r = cg_cm[cg][dim] - bcorner[dim_ind];
+ if (r > 0)
+ {
+ rb2 += r*r;
+ }
+ }
+ /* Rounding gives at most a 16% reduction
+ * in communicated atoms
+ */
+ if (dim_ind >= 1 && (zonei == 1 || zonei == 2))
+ {
+ r = cg_cm[cg][dim0] - corner_round_0;
+ /* This is the first dimension, so always r >= 0 */
+ r2 += r*r;
+ if (bDistMB_pulse)
+ {
+ rb2 += r*r;
+ }
+ }
+ if (dim_ind == 2 && (zonei == 2 || zonei == 3))
+ {
+ r = cg_cm[cg][dim1] - corner_round_1[zone];
+ if (r > 0)
+ {
+ r2 += r*r;
+ }
+ if (bDistMB_pulse)
+ {
+ r = cg_cm[cg][dim1] - bcorner_round_1;
+ if (r > 0)
+ {
+ rb2 += r*r;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Triclinic direction, more complicated */
+ clear_rvec(rn);
+ clear_rvec(rb);
+ /* Rounding, conservative as the skew_fac multiplication
+ * will slightly underestimate the distance.
+ */
+ if (dim_ind >= 1 && (zonei == 1 || zonei == 2))
+ {
+ rn[dim0] = cg_cm[cg][dim0] - corner_round_0;
+ for(i=dim0+1; i<DIM; i++)
+ {
+ rn[dim0] -= cg_cm[cg][i]*v_0[i][dim0];
+ }
+ r2 = rn[dim0]*rn[dim0]*sf2_round[dim0];
+ if (bDistMB_pulse)
+ {
+ rb[dim0] = rn[dim0];
+ rb2 = r2;
+ }
+ /* Take care that the cell planes along dim0 might not
+ * be orthogonal to those along dim1 and dim2.
+ */
+ for(i=1; i<=dim_ind; i++)
+ {
+ dimd = dd->dim[i];
+ if (normal[dim0][dimd] > 0)
+ {
+ rn[dimd] -= rn[dim0]*normal[dim0][dimd];
+ if (bDistMB_pulse)
+ {
+ rb[dimd] -= rb[dim0]*normal[dim0][dimd];
+ }
+ }
+ }
+ }
+ if (dim_ind == 2 && (zonei == 2 || zonei == 3))
+ {
+ rn[dim1] += cg_cm[cg][dim1] - corner_round_1[zone];
+ tric_sh = 0;
+ for(i=dim1+1; i<DIM; i++)
+ {
+ tric_sh -= cg_cm[cg][i]*v_1[i][dim1];
+ }
+ rn[dim1] += tric_sh;
+ if (rn[dim1] > 0)
+ {
+ r2 += rn[dim1]*rn[dim1]*sf2_round[dim1];
+ /* Take care of coupling of the distances
+ * to the planes along dim0 and dim1 through dim2.
+ */
+ r2 -= rn[dim0]*rn[dim1]*skew_fac_01;
+ /* Take care that the cell planes along dim1
+ * might not be orthogonal to that along dim2.
+ */
+ if (normal[dim1][dim2] > 0)
+ {
+ rn[dim2] -= rn[dim1]*normal[dim1][dim2];
+ }
+ }
+ if (bDistMB_pulse)
+ {
+ rb[dim1] +=
+ cg_cm[cg][dim1] - bcorner_round_1 + tric_sh;
+ if (rb[dim1] > 0)
+ {
+ rb2 += rb[dim1]*rb[dim1]*sf2_round[dim1];
+ /* Take care of coupling of the distances
+ * to the planes along dim0 and dim1 through dim2.
+ */
+ rb2 -= rb[dim0]*rb[dim1]*skew_fac_01;
+ /* Take care that the cell planes along dim1
+ * might not be orthogonal to that along dim2.
+ */
+ if (normal[dim1][dim2] > 0)
+ {
+ rb[dim2] -= rb[dim1]*normal[dim1][dim2];
+ }
+ }
+ }
+ }
+ /* The distance along the communication direction */
+ rn[dim] += cg_cm[cg][dim] - corner[dim_ind][zone];
+ tric_sh = 0;
+ for(i=dim+1; i<DIM; i++)
+ {
+ tric_sh -= cg_cm[cg][i]*v_d[i][dim];
+ }
+ rn[dim] += tric_sh;
+ if (rn[dim] > 0)
+ {
+ r2 += rn[dim]*rn[dim]*skew_fac2_d;
+ /* Take care of coupling of the distances
+ * to the planes along dim0 and dim1 through dim2.
+ */
+ if (dim_ind == 1 && zonei == 1)
+ {
+ r2 -= rn[dim0]*rn[dim]*skew_fac_01;
+ }
+ }
+ if (bDistMB_pulse)
+ {
+ clear_rvec(rb);
+ rb[dim] += cg_cm[cg][dim] - bcorner[dim_ind] + tric_sh;
+ if (rb[dim] > 0)
+ {
+ rb2 += rb[dim]*rb[dim]*skew_fac2_d;
+ /* Take care of coupling of the distances
+ * to the planes along dim0 and dim1 through dim2.
+ */
+ if (dim_ind == 1 && zonei == 1)
+ {
+ rb2 -= rb[dim0]*rb[dim]*skew_fac_01;
+ }
+ }
+ }
+ }
+
+ if (r2 < r_comm2 ||
+ (bDistBonded &&
+ ((bDistMB && rb2 < r_bcomm2) ||
+ (bDist2B && r2 < r_bcomm2)) &&
+ (!bBondComm ||
+ (GET_CGINFO_BOND_INTER(fr->cginfo[cg]) &&
+ missing_link(comm->cglink,index_gl[cg],
+ comm->bLocalCG)))))
+ {
+ /* Make an index to the local charge groups */
+ if (nsend+1 > ind->nalloc)
+ {
+ ind->nalloc = over_alloc_large(nsend+1);
+ srenew(ind->index,ind->nalloc);
+ }
+ if (nsend+1 > comm->nalloc_int)
+ {
+ comm->nalloc_int = over_alloc_large(nsend+1);
+ srenew(comm->buf_int,comm->nalloc_int);
+ }
+ ind->index[nsend] = cg;
+ comm->buf_int[nsend] = index_gl[cg];
+ ind->nsend[zone]++;
+ vec_rvec_check_alloc(&comm->vbuf,nsend+1);
+
+ if (dd->ci[dim] == 0)
+ {
+ /* Correct cg_cm for pbc */
+ rvec_add(cg_cm[cg],box[dim],comm->vbuf.v[nsend]);
+ if (bScrew)
+ {
+ comm->vbuf.v[nsend][YY] =
+ box[YY][YY]-comm->vbuf.v[nsend][YY];
+ comm->vbuf.v[nsend][ZZ] =
+ box[ZZ][ZZ]-comm->vbuf.v[nsend][ZZ];
+ }
+ }
+ else
+ {
+ copy_rvec(cg_cm[cg],comm->vbuf.v[nsend]);
+ }
+ nsend++;
+ nat += cgindex[cg+1] - cgindex[cg];
+ }
+ }
+ }
+ /* Clear the counts in case we do not have pbc */
+ for(zone=nzone_send; zone<nzone; zone++)
+ {
+ ind->nsend[zone] = 0;
+ }
+ ind->nsend[nzone] = nsend;
+ ind->nsend[nzone+1] = nat;
+ /* Communicate the number of cg's and atoms to receive */
+ dd_sendrecv_int(dd, dim_ind, dddirBackward,
+ ind->nsend, nzone+2,
+ ind->nrecv, nzone+2);
+
+ /* The rvec buffer is also required for atom buffers of size nsend
+ * in dd_move_x and dd_move_f.
+ */
+ vec_rvec_check_alloc(&comm->vbuf,ind->nsend[nzone+1]);
+
+ if (p > 0)
+ {
+ /* We can receive in place if only the last zone is not empty */
+ for(zone=0; zone<nzone-1; zone++)
+ {
+ if (ind->nrecv[zone] > 0)
+ {
+ cd->bInPlace = FALSE;
+ }
+ }
+ if (!cd->bInPlace)
+ {
+ /* The int buffer is only required here for the cg indices */
+ if (ind->nrecv[nzone] > comm->nalloc_int2)
+ {
+ comm->nalloc_int2 = over_alloc_dd(ind->nrecv[nzone]);
+ srenew(comm->buf_int2,comm->nalloc_int2);
+ }
+ /* The rvec buffer is also required for atom buffers
+ * of size nrecv in dd_move_x and dd_move_f.
+ */
+ i = max(cd->ind[0].nrecv[nzone+1],ind->nrecv[nzone+1]);
+ vec_rvec_check_alloc(&comm->vbuf2,i);
+ }
+ }
+
+ /* Make space for the global cg indices */
+ if (pos_cg + ind->nrecv[nzone] > dd->cg_nalloc
+ || dd->cg_nalloc == 0)
+ {
+ dd->cg_nalloc = over_alloc_dd(pos_cg + ind->nrecv[nzone]);
+ srenew(index_gl,dd->cg_nalloc);
+ srenew(cgindex,dd->cg_nalloc+1);
+ }
+ /* Communicate the global cg indices */
+ if (cd->bInPlace)
+ {
+ recv_i = index_gl + pos_cg;
+ }
+ else
+ {
+ recv_i = comm->buf_int2;
+ }
+ dd_sendrecv_int(dd, dim_ind, dddirBackward,
+ comm->buf_int, nsend,
+ recv_i, ind->nrecv[nzone]);
+
+ /* Make space for cg_cm */
+ if (pos_cg + ind->nrecv[nzone] > fr->cg_nalloc)
+ {
+ dd_realloc_fr_cg(fr,pos_cg + ind->nrecv[nzone]);
+ cg_cm = fr->cg_cm;
+ }
+ /* Communicate cg_cm */
+ if (cd->bInPlace)
+ {
+ recv_vr = cg_cm + pos_cg;
+ }
+ else
+ {
+ recv_vr = comm->vbuf2.v;
+ }
+ dd_sendrecv_rvec(dd, dim_ind, dddirBackward,
+ comm->vbuf.v, nsend,
+ recv_vr, ind->nrecv[nzone]);
+
+ /* Make the charge group index */
+ if (cd->bInPlace)
+ {
+ zone = (p == 0 ? 0 : nzone - 1);
+ while (zone < nzone)
+ {
+ for(cg=0; cg<ind->nrecv[zone]; cg++)
+ {
+ cg_gl = index_gl[pos_cg];
+ fr->cginfo[pos_cg] = ddcginfo(cginfo_mb,cg_gl);
+ nrcg = GET_CGINFO_NATOMS(fr->cginfo[pos_cg]);
+ cgindex[pos_cg+1] = cgindex[pos_cg] + nrcg;
+ if (bBondComm)
+ {
+ /* Update the charge group presence,
+ * so we can use it in the next pass of the loop.
+ */
+ comm->bLocalCG[cg_gl] = TRUE;
+ }
+ pos_cg++;
+ }
+ if (p == 0)
+ {
+ comm->zone_ncg1[nzone+zone] = ind->nrecv[zone];
+ }
+ zone++;
+ zone_cg_range[nzone+zone] = pos_cg;
+ }
+ }
+ else
+ {
+ /* This part of the code is never executed with bBondComm. */
+ merge_cg_buffers(nzone,cd,p,zone_cg_range,
+ index_gl,recv_i,cg_cm,recv_vr,
+ cgindex,fr->cginfo_mb,fr->cginfo);
+ pos_cg += ind->nrecv[nzone];
+ }
+ nat_tot += ind->nrecv[nzone+1];
+ }
+ if (!cd->bInPlace)
+ {
+ /* Store the atom block for easy copying of communication buffers */
+ make_cell2at_index(cd,nzone,zone_cg_range[nzone],cgindex);
+ }
+ nzone += nzone;
+ }
+ dd->index_gl = index_gl;
+ dd->cgindex = cgindex;
+
+ dd->ncg_tot = zone_cg_range[zones->n];
+ dd->nat_tot = nat_tot;
+ comm->nat[ddnatHOME] = dd->nat_home;
+ for(i=ddnatZONE; i<ddnatNR; i++)
+ {
+ comm->nat[i] = dd->nat_tot;
+ }
+
+ if (!bBondComm)
+ {
+ /* We don't need to update cginfo, since that was alrady done above.
+ * So we pass NULL for the forcerec.
+ */
+ dd_set_cginfo(dd->index_gl,dd->ncg_home,dd->ncg_tot,
+ NULL,comm->bLocalCG);
+ }
+
+ if (debug)
+ {
+ fprintf(debug,"Finished setting up DD communication, zones:");
+ for(c=0; c<zones->n; c++)
+ {
+ fprintf(debug," %d",zones->cg_range[c+1]-zones->cg_range[c]);
+ }
+ fprintf(debug,"\n");
+ }
+}
+
+static void set_cg_boundaries(gmx_domdec_zones_t *zones)
+{
+ int c;
+
+ for(c=0; c<zones->nizone; c++)
+ {
+ zones->izone[c].cg1 = zones->cg_range[c+1];
+ zones->izone[c].jcg0 = zones->cg_range[zones->izone[c].j0];
+ zones->izone[c].jcg1 = zones->cg_range[zones->izone[c].j1];
+ }
+}
+
+static int comp_cgsort(const void *a,const void *b)
+{
+ int comp;
+
+ gmx_cgsort_t *cga,*cgb;
+ cga = (gmx_cgsort_t *)a;
+ cgb = (gmx_cgsort_t *)b;
+
+ comp = cga->nsc - cgb->nsc;
+ if (comp == 0)
+ {
+ comp = cga->ind_gl - cgb->ind_gl;
+ }
+
+ return comp;
+}
+
+static void order_int_cg(int n,gmx_cgsort_t *sort,
+ int *a,int *buf)
+{
+ int i;
+
+ /* Order the data */
+ for(i=0; i<n; i++)
+ {
+ buf[i] = a[sort[i].ind];
+ }
+
+ /* Copy back to the original array */
+ for(i=0; i<n; i++)
+ {
+ a[i] = buf[i];
+ }
+}
+
+static void order_vec_cg(int n,gmx_cgsort_t *sort,
+ rvec *v,rvec *buf)
+{
+ int i;
+
+ /* Order the data */
+ for(i=0; i<n; i++)
+ {
+ copy_rvec(v[sort[i].ind],buf[i]);
+ }
+
+ /* Copy back to the original array */
+ for(i=0; i<n; i++)
+ {
+ copy_rvec(buf[i],v[i]);
+ }
+}
+
+static void order_vec_atom(int ncg,int *cgindex,gmx_cgsort_t *sort,
+ rvec *v,rvec *buf)
+{
+ int a,atot,cg,cg0,cg1,i;
+
+ /* Order the data */
+ a = 0;
+ for(cg=0; cg<ncg; cg++)
+ {
+ cg0 = cgindex[sort[cg].ind];
+ cg1 = cgindex[sort[cg].ind+1];
+ for(i=cg0; i<cg1; i++)
+ {
+ copy_rvec(v[i],buf[a]);
+ a++;
+ }
+ }
+ atot = a;
+
+ /* Copy back to the original array */
+ for(a=0; a<atot; a++)
+ {
+ copy_rvec(buf[a],v[a]);
+ }
+}
+
+static void ordered_sort(int nsort2,gmx_cgsort_t *sort2,
+ int nsort_new,gmx_cgsort_t *sort_new,
+ gmx_cgsort_t *sort1)
+{
+ int i1,i2,i_new;
+
+ /* The new indices are not very ordered, so we qsort them */
+ qsort_threadsafe(sort_new,nsort_new,sizeof(sort_new[0]),comp_cgsort);
+
+ /* sort2 is already ordered, so now we can merge the two arrays */
+ i1 = 0;
+ i2 = 0;
+ i_new = 0;
+ while(i2 < nsort2 || i_new < nsort_new)
+ {
+ if (i2 == nsort2)
+ {
+ sort1[i1++] = sort_new[i_new++];
+ }
+ else if (i_new == nsort_new)
+ {
+ sort1[i1++] = sort2[i2++];
+ }
+ else if (sort2[i2].nsc < sort_new[i_new].nsc ||
+ (sort2[i2].nsc == sort_new[i_new].nsc &&
+ sort2[i2].ind_gl < sort_new[i_new].ind_gl))
+ {
+ sort1[i1++] = sort2[i2++];
+ }
+ else
+ {
+ sort1[i1++] = sort_new[i_new++];
+ }
+ }
+}
+
+static void dd_sort_state(gmx_domdec_t *dd,int ePBC,
+ rvec *cgcm,t_forcerec *fr,t_state *state,
+ int ncg_home_old)
+{
+ gmx_domdec_sort_t *sort;
+ gmx_cgsort_t *cgsort,*sort_i;
+ int ncg_new,nsort2,nsort_new,i,cell_index,*ibuf,cgsize;
+ rvec *vbuf;
+
+ sort = dd->comm->sort;
+
+ if (dd->ncg_home > sort->sort_nalloc)
+ {
+ sort->sort_nalloc = over_alloc_dd(dd->ncg_home);
+ srenew(sort->sort1,sort->sort_nalloc);
+ srenew(sort->sort2,sort->sort_nalloc);
+ }
+
+ if (ncg_home_old >= 0)
+ {
+ /* The charge groups that remained in the same ns grid cell
+ * are completely ordered. So we can sort efficiently by sorting
+ * the charge groups that did move into the stationary list.
+ */
+ ncg_new = 0;
+ nsort2 = 0;
+ nsort_new = 0;
+ for(i=0; i<dd->ncg_home; i++)
+ {
+ /* Check if this cg did not move to another node */
+ cell_index = fr->ns.grid->cell_index[i];
+ if (cell_index != 4*fr->ns.grid->ncells)
+ {
+ if (i >= ncg_home_old || cell_index != sort->sort1[i].nsc)
+ {
+ /* This cg is new on this node or moved ns grid cell */
+ if (nsort_new >= sort->sort_new_nalloc)
+ {
+ sort->sort_new_nalloc = over_alloc_dd(nsort_new+1);
+ srenew(sort->sort_new,sort->sort_new_nalloc);
+ }
+ sort_i = &(sort->sort_new[nsort_new++]);
+ }
+ else
+ {
+ /* This cg did not move */
+ sort_i = &(sort->sort2[nsort2++]);
+ }
+ /* Sort on the ns grid cell indices
+ * and the global topology index
+ */
+ sort_i->nsc = cell_index;
+ sort_i->ind_gl = dd->index_gl[i];
+ sort_i->ind = i;
+ ncg_new++;
+ }
+ }
+ if (debug)
+ {
+ fprintf(debug,"ordered sort cgs: stationary %d moved %d\n",
+ nsort2,nsort_new);
+ }
+ /* Sort efficiently */
+ ordered_sort(nsort2,sort->sort2,nsort_new,sort->sort_new,sort->sort1);
+ }
+ else
+ {
+ cgsort = sort->sort1;
+ ncg_new = 0;
+ for(i=0; i<dd->ncg_home; i++)
+ {
+ /* Sort on the ns grid cell indices
+ * and the global topology index
+ */
+ cgsort[i].nsc = fr->ns.grid->cell_index[i];
+ cgsort[i].ind_gl = dd->index_gl[i];
+ cgsort[i].ind = i;
+ if (cgsort[i].nsc != 4*fr->ns.grid->ncells)
+ {
+ ncg_new++;
+ }
+ }
+ if (debug)
+ {
+ fprintf(debug,"qsort cgs: %d new home %d\n",dd->ncg_home,ncg_new);
+ }
+ /* Determine the order of the charge groups using qsort */
+ qsort_threadsafe(cgsort,dd->ncg_home,sizeof(cgsort[0]),comp_cgsort);
+ }
+ cgsort = sort->sort1;
+
+ /* We alloc with the old size, since cgindex is still old */
+ vec_rvec_check_alloc(&dd->comm->vbuf,dd->cgindex[dd->ncg_home]);
+ vbuf = dd->comm->vbuf.v;
+
+ /* Remove the charge groups which are no longer at home here */
+ dd->ncg_home = ncg_new;
+
+ /* Reorder the state */
+ for(i=0; i<estNR; i++)
+ {
+ if (EST_DISTR(i) && state->flags & (1<<i))
+ {
+ switch (i)
+ {
+ case estX:
+ order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->x,vbuf);
+ break;
+ case estV:
+ order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->v,vbuf);
+ break;
+ case estSDX:
+ order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->sd_X,vbuf);
+ break;
+ case estCGP:
+ order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->cg_p,vbuf);
+ break;
+ case estLD_RNG:
+ case estLD_RNGI:
+ case estDISRE_INITF:
+ case estDISRE_RM3TAV:
+ case estORIRE_INITF:
+ case estORIRE_DTAV:
+ /* No ordering required */
+ break;
+ default:
+ gmx_incons("Unknown state entry encountered in dd_sort_state");
+ break;
+ }
+ }
+ }
+ /* Reorder cgcm */
+ order_vec_cg(dd->ncg_home,cgsort,cgcm,vbuf);
+
+ if (dd->ncg_home+1 > sort->ibuf_nalloc)
+ {
+ sort->ibuf_nalloc = over_alloc_dd(dd->ncg_home+1);
+ srenew(sort->ibuf,sort->ibuf_nalloc);
+ }
+ ibuf = sort->ibuf;
+ /* Reorder the global cg index */
+ order_int_cg(dd->ncg_home,cgsort,dd->index_gl,ibuf);
+ /* Reorder the cginfo */
+ order_int_cg(dd->ncg_home,cgsort,fr->cginfo,ibuf);
+ /* Rebuild the local cg index */
+ ibuf[0] = 0;
+ for(i=0; i<dd->ncg_home; i++)
+ {
+ cgsize = dd->cgindex[cgsort[i].ind+1] - dd->cgindex[cgsort[i].ind];
+ ibuf[i+1] = ibuf[i] + cgsize;
+ }
+ for(i=0; i<dd->ncg_home+1; i++)
+ {
+ dd->cgindex[i] = ibuf[i];
+ }
+ /* Set the home atom number */
+ dd->nat_home = dd->cgindex[dd->ncg_home];
+
+ /* Copy the sorted ns cell indices back to the ns grid struct */
+ for(i=0; i<dd->ncg_home; i++)
+ {
+ fr->ns.grid->cell_index[i] = cgsort[i].nsc;
+ }
+ fr->ns.grid->nr = dd->ncg_home;
+}
+
+static void add_dd_statistics(gmx_domdec_t *dd)
+{
+ gmx_domdec_comm_t *comm;
+ int ddnat;
+
+ comm = dd->comm;
+
+ for(ddnat=ddnatZONE; ddnat<ddnatNR; ddnat++)
+ {
+ comm->sum_nat[ddnat-ddnatZONE] +=
+ comm->nat[ddnat] - comm->nat[ddnat-1];
+ }
+ comm->ndecomp++;
+}
+
+void reset_dd_statistics_counters(gmx_domdec_t *dd)
+{
+ gmx_domdec_comm_t *comm;
+ int ddnat;
+
+ comm = dd->comm;
+
+ /* Reset all the statistics and counters for total run counting */
+ for(ddnat=ddnatZONE; ddnat<ddnatNR; ddnat++)
+ {
+ comm->sum_nat[ddnat-ddnatZONE] = 0;
+ }
+ comm->ndecomp = 0;
+ comm->nload = 0;
+ comm->load_step = 0;
+ comm->load_sum = 0;
+ comm->load_max = 0;
+ clear_ivec(comm->load_lim);
+ comm->load_mdf = 0;
+ comm->load_pme = 0;
+}
+
+void print_dd_statistics(t_commrec *cr,t_inputrec *ir,FILE *fplog)
+{
+ gmx_domdec_comm_t *comm;
+ int ddnat;
+ double av;
+
+ comm = cr->dd->comm;
+
+ gmx_sumd(ddnatNR-ddnatZONE,comm->sum_nat,cr);
+
+ if (fplog == NULL)
+ {
+ return;
+ }
+
+ fprintf(fplog,"\n D O M A I N D E C O M P O S I T I O N S T A T I S T I C S\n\n");
+
+ for(ddnat=ddnatZONE; ddnat<ddnatNR; ddnat++)
+ {
+ av = comm->sum_nat[ddnat-ddnatZONE]/comm->ndecomp;
+ switch(ddnat)
+ {
+ case ddnatZONE:
+ fprintf(fplog,
+ " av. #atoms communicated per step for force: %d x %.1f\n",
+ 2,av);
+ break;
+ case ddnatVSITE:
+ if (cr->dd->vsite_comm)
+ {
+ fprintf(fplog,
+ " av. #atoms communicated per step for vsites: %d x %.1f\n",
+ (EEL_PME(ir->coulombtype) || ir->coulombtype==eelEWALD) ? 3 : 2,
+ av);
+ }
+ break;
+ case ddnatCON:
+ if (cr->dd->constraint_comm)
+ {
+ fprintf(fplog,
+ " av. #atoms communicated per step for LINCS: %d x %.1f\n",
+ 1 + ir->nLincsIter,av);
+ }
+ break;
+ default:
+ gmx_incons(" Unknown type for DD statistics");
+ }
+ }
+ fprintf(fplog,"\n");
+
+ if (comm->bRecordLoad && EI_DYNAMICS(ir->eI))
+ {
+ print_dd_load_av(fplog,cr->dd);
+ }
+}
+
+void dd_partition_system(FILE *fplog,
+ gmx_large_int_t step,
+ t_commrec *cr,
+ gmx_bool bMasterState,
+ int nstglobalcomm,
+ t_state *state_global,
+ gmx_mtop_t *top_global,
+ t_inputrec *ir,
+ t_state *state_local,
+ rvec **f,
+ t_mdatoms *mdatoms,
+ gmx_localtop_t *top_local,
+ t_forcerec *fr,
+ gmx_vsite_t *vsite,
+ gmx_shellfc_t shellfc,
+ gmx_constr_t constr,
+ t_nrnb *nrnb,
+ gmx_wallcycle_t wcycle,
+ gmx_bool bVerbose)
+{
+ gmx_domdec_t *dd;
+ gmx_domdec_comm_t *comm;
+ gmx_ddbox_t ddbox={0};
+ t_block *cgs_gl;
+ gmx_large_int_t step_pcoupl;
+ rvec cell_ns_x0,cell_ns_x1;
+ int i,j,n,cg0=0,ncg_home_old=-1,nat_f_novirsum;
+ gmx_bool bBoxChanged,bNStGlobalComm,bDoDLB,bCheckDLB,bTurnOnDLB,bLogLoad;
+ gmx_bool bRedist,bSortCG,bResortAll;
+ ivec ncells_old,np;
+ real grid_density;
+ char sbuf[22];
+
+ dd = cr->dd;
+ comm = dd->comm;
+
+ bBoxChanged = (bMasterState || DEFORM(*ir));
+ if (ir->epc != epcNO)
+ {
+ /* With nstcalcenery > 1 pressure coupling happens.
+ * one step after calculating the energies.
+ * Box scaling happens at the end of the MD step,
+ * after the DD partitioning.
+ * We therefore have to do DLB in the first partitioning
+ * after an MD step where P-coupling occured.
+ * We need to determine the last step in which p-coupling occurred.
+ * MRS -- need to validate this for vv?
+ */
+ n = ir->nstcalcenergy;
+ if (n == 1)
+ {
+ step_pcoupl = step - 1;
+ }
+ else
+ {
+ step_pcoupl = ((step - 1)/n)*n + 1;
+ }
+ if (step_pcoupl >= comm->partition_step)
+ {
+ bBoxChanged = TRUE;
+ }
+ }
+
+ bNStGlobalComm = (step >= comm->partition_step + nstglobalcomm);
+
+ if (!comm->bDynLoadBal)
+ {
+ bDoDLB = FALSE;
+ }
+ else
+ {
+ /* Should we do dynamic load balacing this step?
+ * Since it requires (possibly expensive) global communication,
+ * we might want to do DLB less frequently.
+ */
+ if (bBoxChanged || ir->epc != epcNO)
+ {
+ bDoDLB = bBoxChanged;
+ }
+ else
+ {
+ bDoDLB = bNStGlobalComm;
+ }
+ }
+
+ /* Check if we have recorded loads on the nodes */
+ if (comm->bRecordLoad && dd_load_count(comm))
+ {
+ if (comm->eDLB == edlbAUTO && !comm->bDynLoadBal)
+ {
+ /* Check if we should use DLB at the second partitioning
+ * and every 100 partitionings,
+ * so the extra communication cost is negligible.
+ */
+ n = max(100,nstglobalcomm);
+ bCheckDLB = (comm->n_load_collect == 0 ||
+ comm->n_load_have % n == n-1);
+ }
+ else
+ {
+ bCheckDLB = FALSE;
+ }
+
+ /* Print load every nstlog, first and last step to the log file */
+ bLogLoad = ((ir->nstlog > 0 && step % ir->nstlog == 0) ||
+ comm->n_load_collect == 0 ||
+ (step + ir->nstlist > ir->init_step + ir->nsteps));
+
+ /* Avoid extra communication due to verbose screen output
+ * when nstglobalcomm is set.
+ */
+ if (bDoDLB || bLogLoad || bCheckDLB ||
+ (bVerbose && (ir->nstlist == 0 || nstglobalcomm <= ir->nstlist)))
+ {
+ get_load_distribution(dd,wcycle);
+ if (DDMASTER(dd))
+ {
+ if (bLogLoad)
+ {
+ dd_print_load(fplog,dd,step-1);
+ }
+ if (bVerbose)
+ {
+ dd_print_load_verbose(dd);
+ }
+ }
+ comm->n_load_collect++;
+
+ if (bCheckDLB) {
+ /* Since the timings are node dependent, the master decides */
+ if (DDMASTER(dd))
+ {
+ bTurnOnDLB =
+ (dd_force_imb_perf_loss(dd) >= DD_PERF_LOSS);
+ if (debug)
+ {
+ fprintf(debug,"step %s, imb loss %f\n",
+ gmx_step_str(step,sbuf),
+ dd_force_imb_perf_loss(dd));
+ }
+ }
+ dd_bcast(dd,sizeof(bTurnOnDLB),&bTurnOnDLB);
+ if (bTurnOnDLB)
+ {
+ turn_on_dlb(fplog,cr,step);
+ bDoDLB = TRUE;
+ }
+ }
+ }
+ comm->n_load_have++;
+ }
+
+ cgs_gl = &comm->cgs_gl;
+
+ bRedist = FALSE;
+ if (bMasterState)
+ {
+ /* Clear the old state */
+ clear_dd_indices(dd,0,0);
+
+ set_ddbox(dd,bMasterState,cr,ir,state_global->box,
+ TRUE,cgs_gl,state_global->x,&ddbox);
+
+ get_cg_distribution(fplog,step,dd,cgs_gl,
+ state_global->box,&ddbox,state_global->x);
+
+ dd_distribute_state(dd,cgs_gl,
+ state_global,state_local,f);
+
+ dd_make_local_cgs(dd,&top_local->cgs);
+
+ if (dd->ncg_home > fr->cg_nalloc)
+ {
+ dd_realloc_fr_cg(fr,dd->ncg_home);
+ }
+ calc_cgcm(fplog,0,dd->ncg_home,
+ &top_local->cgs,state_local->x,fr->cg_cm);
+
+ inc_nrnb(nrnb,eNR_CGCM,dd->nat_home);
+
+ dd_set_cginfo(dd->index_gl,0,dd->ncg_home,fr,comm->bLocalCG);
+
+ cg0 = 0;
+ }
+ else if (state_local->ddp_count != dd->ddp_count)
+ {
+ if (state_local->ddp_count > dd->ddp_count)
+ {
+ gmx_fatal(FARGS,"Internal inconsistency state_local->ddp_count (%d) > dd->ddp_count (%d)",state_local->ddp_count,dd->ddp_count);
+ }
+
+ if (state_local->ddp_count_cg_gl != state_local->ddp_count)
+ {
+ gmx_fatal(FARGS,"Internal inconsistency state_local->ddp_count_cg_gl (%d) != state_local->ddp_count (%d)",state_local->ddp_count_cg_gl,state_local->ddp_count);
+ }
+
+ /* Clear the old state */
+ clear_dd_indices(dd,0,0);
+
+ /* Build the new indices */
+ rebuild_cgindex(dd,cgs_gl->index,state_local);
+ make_dd_indices(dd,cgs_gl->index,0);
+
+ /* Redetermine the cg COMs */
+ calc_cgcm(fplog,0,dd->ncg_home,
+ &top_local->cgs,state_local->x,fr->cg_cm);
+
+ inc_nrnb(nrnb,eNR_CGCM,dd->nat_home);
+
+ dd_set_cginfo(dd->index_gl,0,dd->ncg_home,fr,comm->bLocalCG);
+
+ set_ddbox(dd,bMasterState,cr,ir,state_local->box,
+ TRUE,&top_local->cgs,state_local->x,&ddbox);
+
+ bRedist = comm->bDynLoadBal;
+ }
+ else
+ {
+ /* We have the full state, only redistribute the cgs */
+
+ /* Clear the non-home indices */
+ clear_dd_indices(dd,dd->ncg_home,dd->nat_home);
+
+ /* Avoid global communication for dim's without pbc and -gcom */
+ if (!bNStGlobalComm)
+ {
+ copy_rvec(comm->box0 ,ddbox.box0 );
+ copy_rvec(comm->box_size,ddbox.box_size);
+ }
+ set_ddbox(dd,bMasterState,cr,ir,state_local->box,
+ bNStGlobalComm,&top_local->cgs,state_local->x,&ddbox);
+
+ bBoxChanged = TRUE;
+ bRedist = TRUE;
+ }
+ /* For dim's without pbc and -gcom */
+ copy_rvec(ddbox.box0 ,comm->box0 );
+ copy_rvec(ddbox.box_size,comm->box_size);
+
+ set_dd_cell_sizes(dd,&ddbox,dynamic_dd_box(&ddbox,ir),bMasterState,bDoDLB,
+ step,wcycle);
+
+ if (comm->nstDDDumpGrid > 0 && step % comm->nstDDDumpGrid == 0)
+ {
+ write_dd_grid_pdb("dd_grid",step,dd,state_local->box,&ddbox);
+ }
+
+ /* Check if we should sort the charge groups */
+ if (comm->nstSortCG > 0)
+ {
+ bSortCG = (bMasterState ||
+ (bRedist && (step % comm->nstSortCG == 0)));
+ }
+ else
+ {
+ bSortCG = FALSE;
+ }
+
+ ncg_home_old = dd->ncg_home;
+
+ if (bRedist)
+ {
+ cg0 = dd_redistribute_cg(fplog,step,dd,ddbox.tric_dir,
+ state_local,f,fr,mdatoms,
+ !bSortCG,nrnb);
+ }
+
+ get_nsgrid_boundaries(fr->ns.grid,dd,
+ state_local->box,&ddbox,&comm->cell_x0,&comm->cell_x1,
+ dd->ncg_home,fr->cg_cm,
+ cell_ns_x0,cell_ns_x1,&grid_density);
+
+ if (bBoxChanged)
+ {
+ comm_dd_ns_cell_sizes(dd,&ddbox,cell_ns_x0,cell_ns_x1,step);
+ }
+
+ copy_ivec(fr->ns.grid->n,ncells_old);
+ grid_first(fplog,fr->ns.grid,dd,&ddbox,fr->ePBC,
+ state_local->box,cell_ns_x0,cell_ns_x1,
+ fr->rlistlong,grid_density);
+ /* We need to store tric_dir for dd_get_ns_ranges called from ns.c */
+ copy_ivec(ddbox.tric_dir,comm->tric_dir);
+
+ if (bSortCG)
+ {
+ /* Sort the state on charge group position.
+ * This enables exact restarts from this step.
+ * It also improves performance by about 15% with larger numbers
+ * of atoms per node.
+ */
+
+ /* Fill the ns grid with the home cell,
+ * so we can sort with the indices.
+ */
+ set_zones_ncg_home(dd);
+ fill_grid(fplog,&comm->zones,fr->ns.grid,dd->ncg_home,
+ 0,dd->ncg_home,fr->cg_cm);
+
+ /* Check if we can user the old order and ns grid cell indices
+ * of the charge groups to sort the charge groups efficiently.
+ */
+ bResortAll = (bMasterState ||
+ fr->ns.grid->n[XX] != ncells_old[XX] ||
+ fr->ns.grid->n[YY] != ncells_old[YY] ||
+ fr->ns.grid->n[ZZ] != ncells_old[ZZ]);
+
+ if (debug)
+ {
+ fprintf(debug,"Step %s, sorting the %d home charge groups\n",
+ gmx_step_str(step,sbuf),dd->ncg_home);
+ }
+ dd_sort_state(dd,ir->ePBC,fr->cg_cm,fr,state_local,
+ bResortAll ? -1 : ncg_home_old);
+ /* Rebuild all the indices */
+ cg0 = 0;
+ ga2la_clear(dd->ga2la);
+ }
+
+ /* Setup up the communication and communicate the coordinates */
+ setup_dd_communication(dd,state_local->box,&ddbox,fr);
+
+ /* Set the indices */
+ make_dd_indices(dd,cgs_gl->index,cg0);
+
+ /* Set the charge group boundaries for neighbor searching */
+ set_cg_boundaries(&comm->zones);
+
+ /*
+ write_dd_pdb("dd_home",step,"dump",top_global,cr,
+ -1,state_local->x,state_local->box);
+ */
+
+ /* Extract a local topology from the global topology */
+ for(i=0; i<dd->ndim; i++)
+ {
+ np[dd->dim[i]] = comm->cd[i].np;
+ }
+ dd_make_local_top(fplog,dd,&comm->zones,dd->npbcdim,state_local->box,
+ comm->cellsize_min,np,
+ fr,vsite,top_global,top_local);
+
+ /* Set up the special atom communication */
+ n = comm->nat[ddnatZONE];
+ for(i=ddnatZONE+1; i<ddnatNR; i++)
+ {
+ switch(i)
+ {
+ case ddnatVSITE:
+ if (vsite && vsite->n_intercg_vsite)
+ {
+ n = dd_make_local_vsites(dd,n,top_local->idef.il);
+ }
+ break;
+ case ddnatCON:
+ if (dd->bInterCGcons)
+ {
+ /* Only for inter-cg constraints we need special code */
+ n = dd_make_local_constraints(dd,n,top_global,
+ constr,ir->nProjOrder,
+ &top_local->idef.il[F_CONSTR]);
+ }
+ break;
+ default:
+ gmx_incons("Unknown special atom type setup");
+ }
+ comm->nat[i] = n;
+ }
+
+ /* Make space for the extra coordinates for virtual site
+ * or constraint communication.
+ */
+ state_local->natoms = comm->nat[ddnatNR-1];
+ if (state_local->natoms > state_local->nalloc)
+ {
+ dd_realloc_state(state_local,f,state_local->natoms);
+ }
+
+ if (fr->bF_NoVirSum)
+ {
+ if (vsite && vsite->n_intercg_vsite)
+ {
+ nat_f_novirsum = comm->nat[ddnatVSITE];
+ }
+ else
+ {
+ if (EEL_FULL(ir->coulombtype) && dd->n_intercg_excl > 0)
+ {
+ nat_f_novirsum = dd->nat_tot;
+ }
+ else
+ {
+ nat_f_novirsum = dd->nat_home;
+ }
+ }
+ }
+ else
+ {
+ nat_f_novirsum = 0;
+ }
+
+ /* Set the number of atoms required for the force calculation.
+ * Forces need to be constrained when using a twin-range setup
+ * or with energy minimization. For simple simulations we could
+ * avoid some allocation, zeroing and copying, but this is
+ * probably not worth the complications ande checking.
+ */
+ forcerec_set_ranges(fr,dd->ncg_home,dd->ncg_tot,
+ dd->nat_tot,comm->nat[ddnatCON],nat_f_novirsum);
+
+ /* We make the all mdatoms up to nat_tot_con.
+ * We could save some work by only setting invmass
+ * between nat_tot and nat_tot_con.
+ */
+ /* This call also sets the new number of home particles to dd->nat_home */
+ atoms2md(top_global,ir,
+ comm->nat[ddnatCON],dd->gatindex,0,dd->nat_home,mdatoms);
+
+ /* Now we have the charges we can sort the FE interactions */
+ dd_sort_local_top(dd,mdatoms,top_local);
+
+ if (shellfc)
+ {
+ /* Make the local shell stuff, currently no communication is done */
+ make_local_shells(cr,mdatoms,shellfc);
+ }
+
+ if (ir->implicit_solvent)
+ {
+ make_local_gb(cr,fr->born,ir->gb_algorithm);
+ }
+
+ if (!(cr->duty & DUTY_PME))
+ {
+ /* Send the charges to our PME only node */
+ gmx_pme_send_q(cr,mdatoms->nChargePerturbed,
+ mdatoms->chargeA,mdatoms->chargeB,
+ dd_pme_maxshift_x(dd),dd_pme_maxshift_y(dd));
+ }
+
+ if (constr)
+ {
+ set_constraints(constr,top_local,ir,mdatoms,cr);
+ }
+
+ if (ir->ePull != epullNO)
+ {
+ /* Update the local pull groups */
+ dd_make_local_pull_groups(dd,ir->pull,mdatoms);
+ }
+
+ if (ir->bRot)
+ {
+ /* Update the local rotation groups */
+ dd_make_local_rotation_groups(dd,ir->rot);
+ }
+
+
+ add_dd_statistics(dd);
+
+ /* Make sure we only count the cycles for this DD partitioning */
+ clear_dd_cycle_counts(dd);
+
+ /* Because the order of the atoms might have changed since
+ * the last vsite construction, we need to communicate the constructing
+ * atom coordinates again (for spreading the forces this MD step).
+ */
+ dd_move_x_vsites(dd,state_local->box,state_local->x);
+
+ if (comm->nstDDDump > 0 && step % comm->nstDDDump == 0)
+ {
+ dd_move_x(dd,state_local->box,state_local->x);
+ write_dd_pdb("dd_dump",step,"dump",top_global,cr,
+ -1,state_local->x,state_local->box);
+ }
+
+ /* Store the partitioning step */
+ comm->partition_step = step;
+
+ /* Increase the DD partitioning counter */
+ dd->ddp_count++;
+ /* The state currently matches this DD partitioning count, store it */
+ state_local->ddp_count = dd->ddp_count;
+ if (bMasterState)
+ {
+ /* The DD master node knows the complete cg distribution,
+ * store the count so we can possibly skip the cg info communication.
+ */
+ comm->master_cg_ddp_count = (bSortCG ? 0 : dd->ddp_count);
+ }
+
+ if (comm->DD_debug > 0)
+ {
+ /* Set the env var GMX_DD_DEBUG if you suspect corrupted indices */
+ check_index_consistency(dd,top_global->natoms,ncg_mtop(top_global),
+ "after partitioning");
+ }
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include "gmx_wallcycle.h"
+#include "gmx_cyclecounter.h"
+#include "smalloc.h"
+#include "gmx_fatal.h"
+
+#ifdef GMX_LIB_MPI
+#include <mpi.h>
+#endif
+#ifdef GMX_THREADS
+#include "tmpi.h"
+#endif
+
+typedef struct
+{
+ int n;
+ gmx_cycles_t c;
+ gmx_cycles_t start;
+ gmx_cycles_t last;
+} wallcc_t;
+
+typedef struct gmx_wallcycle
+{
+ wallcc_t *wcc;
+ /* variables for testing/debugging */
+ gmx_bool wc_barrier;
+ wallcc_t *wcc_all;
+ int wc_depth;
+ int ewc_prev;
+ gmx_cycles_t cycle_prev;
+ gmx_large_int_t reset_counters;
+#ifdef GMX_MPI
+ MPI_Comm mpi_comm_mygroup;
+#endif
+} gmx_wallcycle_t_t;
+
+/* Each name should not exceed 19 characters */
+static const char *wcn[ewcNR] =
+{ "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load", "DD comm. bounds", "Vsite constr.", "Send X to PME", "Comm. coord.", "Neighbor search", "Born radii", "Force", "Wait + Comm. F", "PME mesh", "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME solve", "Wait + Comm. X/F", "Wait + Recv. PME F", "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies", "Enforced rotation", "Test" };
+
+gmx_bool wallcycle_have_counter(void)
+{
+ return gmx_cycles_have_counter();
+}
+
+gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr)
+{
+ gmx_wallcycle_t wc;
+
+
+ if (!wallcycle_have_counter())
+ {
+ return NULL;
+ }
+
+ snew(wc,1);
+
+ wc->wc_barrier = FALSE;
+ wc->wcc_all = NULL;
+ wc->wc_depth = 0;
+ wc->ewc_prev = -1;
+ wc->reset_counters = resetstep;
+
+#ifdef GMX_MPI
+ if (PAR(cr) && getenv("GMX_CYCLE_BARRIER") != NULL)
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"\nWill call MPI_Barrier before each cycle start/stop call\n\n");
+ }
+ wc->wc_barrier = TRUE;
+ wc->mpi_comm_mygroup = cr->mpi_comm_mygroup;
+ }
+#endif
+
+ snew(wc->wcc,ewcNR);
+ if (getenv("GMX_CYCLE_ALL") != NULL)
+ {
+/*#ifndef GMX_THREADS*/
+ if (fplog)
+ {
+ fprintf(fplog,"\nWill time all the code during the run\n\n");
+ }
+ snew(wc->wcc_all,ewcNR*ewcNR);
+/*#else*/
+ gmx_fatal(FARGS, "GMX_CYCLE_ALL is incompatible with threaded code");
+/*#endif*/
+ }
+
+ return wc;
+}
+
+void wallcycle_destroy(gmx_wallcycle_t wc)
+{
+ if (wc == NULL)
+ {
+ return;
+ }
+
+ if (wc->wcc != NULL)
+ {
+ sfree(wc->wcc);
+ }
+ if (wc->wcc_all != NULL)
+ {
+ sfree(wc->wcc_all);
+ }
+ sfree(wc);
+}
+
+static void wallcycle_all_start(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
+{
+ wc->ewc_prev = ewc;
+ wc->cycle_prev = cycle;
+}
+
+static void wallcycle_all_stop(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
+{
+ wc->wcc_all[wc->ewc_prev*ewcNR+ewc].n += 1;
+ wc->wcc_all[wc->ewc_prev*ewcNR+ewc].c += cycle - wc->cycle_prev;
+}
+
+void wallcycle_start(gmx_wallcycle_t wc, int ewc)
+{
+ gmx_cycles_t cycle;
+
+ if (wc == NULL)
+ {
+ return;
+ }
+
+#ifdef GMX_MPI
+ if (wc->wc_barrier)
+ {
+ MPI_Barrier(wc->mpi_comm_mygroup);
+ }
+#endif
+
+ cycle = gmx_cycles_read();
+ wc->wcc[ewc].start = cycle;
+ if (wc->wcc_all != NULL)
+ {
+ wc->wc_depth++;
+ if (ewc == ewcRUN)
+ {
+ wallcycle_all_start(wc,ewc,cycle);
+ }
+ else if (wc->wc_depth == 3)
+ {
+ wallcycle_all_stop(wc,ewc,cycle);
+ }
+ }
+}
+
+double wallcycle_stop(gmx_wallcycle_t wc, int ewc)
+{
+ gmx_cycles_t cycle,last;
+
+ if (wc == NULL)
+ {
+ return 0;
+ }
+
+#ifdef GMX_MPI
+ if (wc->wc_barrier)
+ {
+ MPI_Barrier(wc->mpi_comm_mygroup);
+ }
+#endif
+
+ cycle = gmx_cycles_read();
+ last = cycle - wc->wcc[ewc].start;
+ wc->wcc[ewc].c += last;
+ wc->wcc[ewc].n++;
+ if (wc->wcc_all)
+ {
+ wc->wc_depth--;
+ if (ewc == ewcRUN)
+ {
+ wallcycle_all_stop(wc,ewc,cycle);
+ }
+ else if (wc->wc_depth == 2)
+ {
+ wallcycle_all_start(wc,ewc,cycle);
+ }
+ }
+
+ return last;
+}
+
+void wallcycle_reset_all(gmx_wallcycle_t wc)
+{
+ int i;
+
+ if (wc == NULL)
+ {
+ return;
+ }
+
+ for(i=0; i<ewcNR; i++)
+ {
+ wc->wcc[i].n = 0;
+ wc->wcc[i].c = 0;
+ wc->wcc[i].start = 0;
+ wc->wcc[i].last = 0;
+ }
+}
+
+void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc,double cycles[])
+{
+ wallcc_t *wcc;
+ double cycles_n[ewcNR],buf[ewcNR],*cyc_all,*buf_all;
+ int i;
+
+ if (wc == NULL)
+ {
+ return;
+ }
+
+ wcc = wc->wcc;
+
+ if (wcc[ewcDDCOMMLOAD].n > 0)
+ {
+ wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMLOAD].c;
+ }
+ if (wcc[ewcDDCOMMBOUND].n > 0)
+ {
+ wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMBOUND].c;
+ }
+ if (cr->npmenodes == 0)
+ {
+ /* All nodes do PME (or no PME at all) */
+ if (wcc[ewcPMEMESH].n > 0)
+ {
+ wcc[ewcFORCE].c -= wcc[ewcPMEMESH].c;
+ }
+ }
+ else
+ {
+ /* The are PME-only nodes */
+ if (wcc[ewcPMEMESH].n > 0)
+ {
+ /* This must be a PME only node, calculate the Wait + Comm. time */
+ wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
+ }
+ }
+
+ /* Store the cycles in a double buffer for summing */
+ for(i=0; i<ewcNR; i++)
+ {
+ cycles_n[i] = (double)wcc[i].n;
+ cycles[i] = (double)wcc[i].c;
+ }
+
+#ifdef GMX_MPI
+ if (cr->nnodes > 1)
+ {
+ MPI_Allreduce(cycles_n,buf,ewcNR,MPI_DOUBLE,MPI_MAX,
+ cr->mpi_comm_mysim);
+ for(i=0; i<ewcNR; i++)
+ {
+ wcc[i].n = (int)(buf[i] + 0.5);
+ }
+ MPI_Allreduce(cycles,buf,ewcNR,MPI_DOUBLE,MPI_SUM,
+ cr->mpi_comm_mysim);
+ for(i=0; i<ewcNR; i++)
+ {
+ cycles[i] = buf[i];
+ }
+
+ if (wc->wcc_all != NULL)
+ {
+ snew(cyc_all,ewcNR*ewcNR);
+ snew(buf_all,ewcNR*ewcNR);
+ for(i=0; i<ewcNR*ewcNR; i++)
+ {
+ cyc_all[i] = wc->wcc_all[i].c;
+ }
+ MPI_Allreduce(cyc_all,buf_all,ewcNR*ewcNR,MPI_DOUBLE,MPI_SUM,
+ cr->mpi_comm_mysim);
+ for(i=0; i<ewcNR*ewcNR; i++)
+ {
+ wc->wcc_all[i].c = buf_all[i];
+ }
+ sfree(buf_all);
+ sfree(cyc_all);
+ }
+ }
+#endif
+}
+
+static void print_cycles(FILE *fplog, double c2t, const char *name, int nnodes,
+ int n, double c, double tot)
+{
+ char num[11];
+
+ if (c > 0)
+ {
+ if (n > 0)
+ {
+ sprintf(num,"%10d",n);
+ }
+ else
+ {
+ sprintf(num," ");
+ }
+ fprintf(fplog," %-19s %4d %10s %12.3f %10.1f %5.1f\n",
+ name,nnodes,num,c*1e-9,c*c2t,100*c/tot);
+ }
+}
+
+static gmx_bool subdivision(int ewc)
+{
+ return (ewc >= ewcPME_REDISTXF && ewc <= ewcPME_SOLVE);
+}
+
+void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
+ gmx_wallcycle_t wc, double cycles[])
+{
+ double c2t,tot,sum;
+ int i,j,npp;
+ char buf[STRLEN];
+ const char *myline = "-----------------------------------------------------------------------";
+
+ if (wc == NULL)
+ {
+ return;
+ }
+
+ if (npme > 0)
+ {
+ npp = nnodes - npme;
+ }
+ else
+ {
+ npp = nnodes;
+ npme = nnodes;
+ }
+ tot = cycles[ewcRUN];
+ /* Conversion factor from cycles to seconds */
+ if (tot > 0)
+ {
+ c2t = nnodes*realtime/tot;
+ }
+ else
+ {
+ c2t = 0;
+ }
+
+ fprintf(fplog,"\n R E A L C Y C L E A N D T I M E A C C O U N T I N G\n\n");
+
+ fprintf(fplog," Computing: Nodes Number G-Cycles Seconds %c\n",'%');
+ fprintf(fplog,"%s\n",myline);
+ sum = 0;
+ for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
+ {
+ if (!subdivision(i))
+ {
+ print_cycles(fplog,c2t,wcn[i],
+ (i==ewcPMEMESH || i==ewcPMEWAITCOMM) ? npme : npp,
+ wc->wcc[i].n,cycles[i],tot);
+ sum += cycles[i];
+ }
+ }
+ if (wc->wcc_all != NULL)
+ {
+ for(i=0; i<ewcNR; i++)
+ {
+ for(j=0; j<ewcNR; j++)
+ {
+ sprintf(buf,"%-9s",wcn[i]);
+ buf[9] = ' ';
+ sprintf(buf+10,"%-9s",wcn[j]);
+ buf[19] = '\0';
+ print_cycles(fplog,c2t,buf,
+ (i==ewcPMEMESH || i==ewcPMEWAITCOMM) ? npme : npp,
+ wc->wcc_all[i*ewcNR+j].n,
+ wc->wcc_all[i*ewcNR+j].c,
+ tot);
+ }
+ }
+ }
+ print_cycles(fplog,c2t,"Rest",npp,0,tot-sum,tot);
+ fprintf(fplog,"%s\n",myline);
+ print_cycles(fplog,c2t,"Total",nnodes,0,tot,tot);
+ fprintf(fplog,"%s\n",myline);
+
+ if (wc->wcc[ewcPMEMESH].n > 0)
+ {
+ fprintf(fplog,"%s\n",myline);
+ for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
+ {
+ if (subdivision(i))
+ {
+ print_cycles(fplog,c2t,wcn[i],
+ (i>=ewcPMEMESH || i<=ewcPME_SOLVE) ? npme : npp,
+ wc->wcc[i].n,cycles[i],tot);
+ }
+ }
+ fprintf(fplog,"%s\n",myline);
+ }
+
+ if (cycles[ewcMoveE] > tot*0.05)
+ {
+ sprintf(buf,
+ "NOTE: %d %% of the run time was spent communicating energies,\n"
+ " you might want to use the -gcom option of mdrun\n",
+ (int)(100*cycles[ewcMoveE]/tot+0.5));
+ if (fplog)
+ {
+ fprintf(fplog,"\n%s\n",buf);
+ }
+ /* Only the sim master calls this function, so always print to stderr */
+ fprintf(stderr,"\n%s\n",buf);
+ }
+}
+
+extern gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
+{
+ if (wc == NULL)
+ {
+ return -1;
+ }
+
+ return wc->reset_counters;
+}
+
+extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters)
+{
+ if (wc == NULL)
+ return;
+
+ wc->reset_counters = reset_counters;
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <float.h>
+#include "typedefs.h"
+#include "string2.h"
+#include "mdebin.h"
+#include "smalloc.h"
+#include "physics.h"
+#include "enxio.h"
+#include "vec.h"
+#include "disre.h"
+#include "main.h"
+#include "network.h"
+#include "names.h"
+#include "orires.h"
+#include "constr.h"
+#include "mtop_util.h"
+#include "xvgr.h"
+#include "gmxfio.h"
+
+#include "mdebin_bar.h"
+
+
+static const char *conrmsd_nm[] = { "Constr. rmsd", "Constr.2 rmsd" };
+
+static const char *boxs_nm[] = { "Box-X", "Box-Y", "Box-Z" };
+
+static const char *tricl_boxs_nm[] = {
+ "Box-XX", "Box-YY", "Box-ZZ",
+ "Box-YX", "Box-ZX", "Box-ZY"
+};
+
+static const char *vol_nm[] = { "Volume" };
+
+static const char *dens_nm[] = {"Density" };
+
+static const char *pv_nm[] = {"pV" };
+
+static const char *enthalpy_nm[] = {"Enthalpy" };
+
+static const char *boxvel_nm[] = {
+ "Box-Vel-XX", "Box-Vel-YY", "Box-Vel-ZZ",
+ "Box-Vel-YX", "Box-Vel-ZX", "Box-Vel-ZY"
+};
+
+#define NBOXS asize(boxs_nm)
+#define NTRICLBOXS asize(tricl_boxs_nm)
+
+static gmx_bool bTricl,bDynBox;
+static int f_nre=0,epc,etc,nCrmsd;
+
+
+
+
+
+t_mdebin *init_mdebin(ener_file_t fp_ene,
+ const gmx_mtop_t *mtop,
+ const t_inputrec *ir,
+ FILE *fp_dhdl)
+{
+ const char *ener_nm[F_NRE];
+ static const char *vir_nm[] = {
+ "Vir-XX", "Vir-XY", "Vir-XZ",
+ "Vir-YX", "Vir-YY", "Vir-YZ",
+ "Vir-ZX", "Vir-ZY", "Vir-ZZ"
+ };
+ static const char *sv_nm[] = {
+ "ShakeVir-XX", "ShakeVir-XY", "ShakeVir-XZ",
+ "ShakeVir-YX", "ShakeVir-YY", "ShakeVir-YZ",
+ "ShakeVir-ZX", "ShakeVir-ZY", "ShakeVir-ZZ"
+ };
+ static const char *fv_nm[] = {
+ "ForceVir-XX", "ForceVir-XY", "ForceVir-XZ",
+ "ForceVir-YX", "ForceVir-YY", "ForceVir-YZ",
+ "ForceVir-ZX", "ForceVir-ZY", "ForceVir-ZZ"
+ };
+ static const char *pres_nm[] = {
+ "Pres-XX","Pres-XY","Pres-XZ",
+ "Pres-YX","Pres-YY","Pres-YZ",
+ "Pres-ZX","Pres-ZY","Pres-ZZ"
+ };
+ static const char *surft_nm[] = {
+ "#Surf*SurfTen"
+ };
+ static const char *mu_nm[] = {
+ "Mu-X", "Mu-Y", "Mu-Z"
+ };
+ static const char *vcos_nm[] = {
+ "2CosZ*Vel-X"
+ };
+ static const char *visc_nm[] = {
+ "1/Viscosity"
+ };
+ static const char *baro_nm[] = {
+ "Barostat"
+ };
+
+ char **grpnms;
+ const gmx_groups_t *groups;
+ char **gnm;
+ char buf[256];
+ const char *bufi;
+ t_mdebin *md;
+ int i,j,ni,nj,n,nh,k,kk,ncon,nset;
+ gmx_bool bBHAM,bNoseHoover,b14;
+
+ snew(md,1);
+
+ if (EI_DYNAMICS(ir->eI))
+ {
+ md->delta_t = ir->delta_t;
+ }
+ else
+ {
+ md->delta_t = 0;
+ }
+
+ groups = &mtop->groups;
+
+ bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
+ b14 = (gmx_mtop_ftype_count(mtop,F_LJ14) > 0 ||
+ gmx_mtop_ftype_count(mtop,F_LJC14_Q) > 0);
+
+ ncon = gmx_mtop_ftype_count(mtop,F_CONSTR);
+ nset = gmx_mtop_ftype_count(mtop,F_SETTLE);
+ md->bConstr = (ncon > 0 || nset > 0);
+ md->bConstrVir = FALSE;
+ if (md->bConstr) {
+ if (ncon > 0 && ir->eConstrAlg == econtLINCS) {
+ if (ir->eI == eiSD2)
+ md->nCrmsd = 2;
+ else
+ md->nCrmsd = 1;
+ }
+ md->bConstrVir = (getenv("GMX_CONSTRAINTVIR") != NULL);
+ } else {
+ md->nCrmsd = 0;
+ }
+
+ /* Energy monitoring */
+ for(i=0;i<egNR;i++)
+ {
+ md->bEInd[i]=FALSE;
+ }
+
+#ifndef GMX_OPENMM
+ for(i=0; i<F_NRE; i++)
+ {
+ md->bEner[i] = FALSE;
+ if (i == F_LJ)
+ md->bEner[i] = !bBHAM;
+ else if (i == F_BHAM)
+ md->bEner[i] = bBHAM;
+ else if (i == F_EQM)
+ md->bEner[i] = ir->bQMMM;
+ else if (i == F_COUL_LR)
+ md->bEner[i] = (ir->rcoulomb > ir->rlist);
+ else if (i == F_LJ_LR)
+ md->bEner[i] = (!bBHAM && ir->rvdw > ir->rlist);
+ else if (i == F_BHAM_LR)
+ md->bEner[i] = (bBHAM && ir->rvdw > ir->rlist);
+ else if (i == F_RF_EXCL)
+ md->bEner[i] = (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC);
+ else if (i == F_COUL_RECIP)
+ md->bEner[i] = EEL_FULL(ir->coulombtype);
+ else if (i == F_LJ14)
+ md->bEner[i] = b14;
+ else if (i == F_COUL14)
+ md->bEner[i] = b14;
+ else if (i == F_LJC14_Q || i == F_LJC_PAIRS_NB)
+ md->bEner[i] = FALSE;
+ else if ((i == F_DVDL) || (i == F_DKDL))
+ md->bEner[i] = (ir->efep != efepNO);
+ else if (i == F_DHDL_CON)
+ md->bEner[i] = (ir->efep != efepNO && md->bConstr);
+ else if ((interaction_function[i].flags & IF_VSITE) ||
+ (i == F_CONSTR) || (i == F_CONSTRNC) || (i == F_SETTLE))
+ md->bEner[i] = FALSE;
+ else if ((i == F_COUL_SR) || (i == F_EPOT) || (i == F_PRES) || (i==F_EQM))
+ md->bEner[i] = TRUE;
+ else if ((i == F_GBPOL) && ir->implicit_solvent==eisGBSA)
+ md->bEner[i] = TRUE;
+ else if ((i == F_NPSOLVATION) && ir->implicit_solvent==eisGBSA && (ir->sa_algorithm != esaNO))
+ md->bEner[i] = TRUE;
+ else if ((i == F_GB12) || (i == F_GB13) || (i == F_GB14))
+ md->bEner[i] = FALSE;
+ else if ((i == F_ETOT) || (i == F_EKIN) || (i == F_TEMP))
+ md->bEner[i] = EI_DYNAMICS(ir->eI);
+ else if (i==F_VTEMP)
+ md->bEner[i] = (EI_DYNAMICS(ir->eI) && getenv("GMX_VIRIAL_TEMPERATURE"));
+ else if (i == F_DISPCORR || i == F_PDISPCORR)
+ md->bEner[i] = (ir->eDispCorr != edispcNO);
+ else if (i == F_DISRESVIOL)
+ md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_DISRES) > 0);
+ else if (i == F_ORIRESDEV)
+ md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0);
+ else if (i == F_CONNBONDS)
+ md->bEner[i] = FALSE;
+ else if (i == F_COM_PULL)
+ md->bEner[i] = (ir->ePull == epullUMBRELLA || ir->ePull == epullCONST_F || ir->bRot);
+ else if (i == F_ECONSERVED)
+ md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
+ (ir->epc == epcNO || ir->epc==epcMTTK));
+ else
+ md->bEner[i] = (gmx_mtop_ftype_count(mtop,i) > 0);
+ }
+#else
+ /* OpenMM always produces only the following 4 energy terms */
+ md->bEner[F_EPOT] = TRUE;
+ md->bEner[F_EKIN] = TRUE;
+ md->bEner[F_ETOT] = TRUE;
+ md->bEner[F_TEMP] = TRUE;
+#endif
+
+ md->f_nre=0;
+ for(i=0; i<F_NRE; i++)
+ {
+ if (md->bEner[i])
+ {
+ /* FIXME: The constness should not be cast away */
+ /*ener_nm[f_nre]=(char *)interaction_function[i].longname;*/
+ ener_nm[md->f_nre]=interaction_function[i].longname;
+ md->f_nre++;
+ }
+ }
+
+ md->epc = ir->epc;
+ for (i=0;i<DIM;i++)
+ {
+ for (j=0;j<DIM;j++)
+ {
+ md->ref_p[i][j] = ir->ref_p[i][j];
+ }
+ }
+ md->bTricl = TRICLINIC(ir->compress) || TRICLINIC(ir->deform);
+ md->bDynBox = DYNAMIC_BOX(*ir);
+ md->etc = ir->etc;
+ md->bNHC_trotter = IR_NVT_TROTTER(ir);
+ md->bMTTK = IR_NPT_TROTTER(ir);
+
+ md->ebin = mk_ebin();
+ /* Pass NULL for unit to let get_ebin_space determine the units
+ * for interaction_function[i].longname
+ */
+ md->ie = get_ebin_space(md->ebin,md->f_nre,ener_nm,NULL);
+ if (md->nCrmsd)
+ {
+ /* This should be called directly after the call for md->ie,
+ * such that md->iconrmsd follows directly in the list.
+ */
+ md->iconrmsd = get_ebin_space(md->ebin,md->nCrmsd,conrmsd_nm,"");
+ }
+ if (md->bDynBox)
+ {
+ md->ib = get_ebin_space(md->ebin,
+ md->bTricl ? NTRICLBOXS : NBOXS,
+ md->bTricl ? tricl_boxs_nm : boxs_nm,
+ unit_length);
+ md->ivol = get_ebin_space(md->ebin, 1, vol_nm, unit_volume);
+ md->idens = get_ebin_space(md->ebin, 1, dens_nm, unit_density_SI);
+ md->ipv = get_ebin_space(md->ebin, 1, pv_nm, unit_energy);
+ md->ienthalpy = get_ebin_space(md->ebin, 1, enthalpy_nm, unit_energy);
+ }
+ if (md->bConstrVir)
+ {
+ md->isvir = get_ebin_space(md->ebin,asize(sv_nm),sv_nm,unit_energy);
+ md->ifvir = get_ebin_space(md->ebin,asize(fv_nm),fv_nm,unit_energy);
+ }
+ md->ivir = get_ebin_space(md->ebin,asize(vir_nm),vir_nm,unit_energy);
+ md->ipres = get_ebin_space(md->ebin,asize(pres_nm),pres_nm,unit_pres_bar);
+ md->isurft = get_ebin_space(md->ebin,asize(surft_nm),surft_nm,
+ unit_surft_bar);
+ if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
+ {
+ md->ipc = get_ebin_space(md->ebin,md->bTricl ? 6 : 3,
+ boxvel_nm,unit_vel);
+ }
+ md->imu = get_ebin_space(md->ebin,asize(mu_nm),mu_nm,unit_dipole_D);
+ if (ir->cos_accel != 0)
+ {
+ md->ivcos = get_ebin_space(md->ebin,asize(vcos_nm),vcos_nm,unit_vel);
+ md->ivisc = get_ebin_space(md->ebin,asize(visc_nm),visc_nm,
+ unit_invvisc_SI);
+ }
+
+ /* Energy monitoring */
+ for(i=0;i<egNR;i++)
+ {
+ md->bEInd[i] = FALSE;
+ }
+ md->bEInd[egCOULSR] = TRUE;
+ md->bEInd[egLJSR ] = TRUE;
+
+ if (ir->rcoulomb > ir->rlist)
+ {
+ md->bEInd[egCOULLR] = TRUE;
+ }
+ if (!bBHAM)
+ {
+ if (ir->rvdw > ir->rlist)
+ {
+ md->bEInd[egLJLR] = TRUE;
+ }
+ }
+ else
+ {
+ md->bEInd[egLJSR] = FALSE;
+ md->bEInd[egBHAMSR] = TRUE;
+ if (ir->rvdw > ir->rlist)
+ {
+ md->bEInd[egBHAMLR] = TRUE;
+ }
+ }
+ if (b14)
+ {
+ md->bEInd[egLJ14] = TRUE;
+ md->bEInd[egCOUL14] = TRUE;
+ }
+ md->nEc=0;
+ for(i=0; (i<egNR); i++)
+ {
+ if (md->bEInd[i])
+ {
+ md->nEc++;
+ }
+ }
+
+ n=groups->grps[egcENER].nr;
+ md->nEg=n;
+ md->nE=(n*(n+1))/2;
+ snew(md->igrp,md->nE);
+ if (md->nE > 1)
+ {
+ n=0;
+ snew(gnm,md->nEc);
+ for(k=0; (k<md->nEc); k++)
+ {
+ snew(gnm[k],STRLEN);
+ }
+ for(i=0; (i<groups->grps[egcENER].nr); i++)
+ {
+ ni=groups->grps[egcENER].nm_ind[i];
+ for(j=i; (j<groups->grps[egcENER].nr); j++)
+ {
+ nj=groups->grps[egcENER].nm_ind[j];
+ for(k=kk=0; (k<egNR); k++)
+ {
+ if (md->bEInd[k])
+ {
+ sprintf(gnm[kk],"%s:%s-%s",egrp_nm[k],
+ *(groups->grpname[ni]),*(groups->grpname[nj]));
+ kk++;
+ }
+ }
+ md->igrp[n]=get_ebin_space(md->ebin,md->nEc,
+ (const char **)gnm,unit_energy);
+ n++;
+ }
+ }
+ for(k=0; (k<md->nEc); k++)
+ {
+ sfree(gnm[k]);
+ }
+ sfree(gnm);
+
+ if (n != md->nE)
+ {
+ gmx_incons("Number of energy terms wrong");
+ }
+ }
+
+ md->nTC=groups->grps[egcTC].nr;
+ md->nNHC = ir->opts.nhchainlength; /* shorthand for number of NH chains */
+ if (md->bMTTK)
+ {
+ md->nTCP = 1; /* assume only one possible coupling system for barostat
+ for now */
+ }
+ else
+ {
+ md->nTCP = 0;
+ }
+
+ if (md->etc == etcNOSEHOOVER) {
+ if (md->bNHC_trotter) {
+ md->mde_n = 2*md->nNHC*md->nTC;
+ }
+ else
+ {
+ md->mde_n = 2*md->nTC;
+ }
+ if (md->epc == epcMTTK)
+ {
+ md->mdeb_n = 2*md->nNHC*md->nTCP;
+ }
+ } else {
+ md->mde_n = md->nTC;
+ md->mdeb_n = 0;
+ }
+
+ snew(md->tmp_r,md->mde_n);
+ snew(md->tmp_v,md->mde_n);
+ snew(md->grpnms,md->mde_n);
+ grpnms = md->grpnms;
+
+ for(i=0; (i<md->nTC); i++)
+ {
+ ni=groups->grps[egcTC].nm_ind[i];
+ sprintf(buf,"T-%s",*(groups->grpname[ni]));
+ grpnms[i]=strdup(buf);
+ }
+ md->itemp=get_ebin_space(md->ebin,md->nTC,(const char **)grpnms,
+ unit_temp_K);
+
+ bNoseHoover = (getenv("GMX_NOSEHOOVER_CHAINS") != NULL); /* whether to print Nose-Hoover chains */
+
+ if (md->etc == etcNOSEHOOVER)
+ {
+ if (bNoseHoover)
+ {
+ if (md->bNHC_trotter)
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ ni=groups->grps[egcTC].nm_ind[i];
+ bufi = *(groups->grpname[ni]);
+ for(j=0; (j<md->nNHC); j++)
+ {
+ sprintf(buf,"Xi-%d-%s",j,bufi);
+ grpnms[2*(i*md->nNHC+j)]=strdup(buf);
+ sprintf(buf,"vXi-%d-%s",j,bufi);
+ grpnms[2*(i*md->nNHC+j)+1]=strdup(buf);
+ }
+ }
+ md->itc=get_ebin_space(md->ebin,md->mde_n,
+ (const char **)grpnms,unit_invtime);
+ if (md->bMTTK)
+ {
+ for(i=0; (i<md->nTCP); i++)
+ {
+ bufi = baro_nm[0]; /* All barostat DOF's together for now. */
+ for(j=0; (j<md->nNHC); j++)
+ {
+ sprintf(buf,"Xi-%d-%s",j,bufi);
+ grpnms[2*(i*md->nNHC+j)]=strdup(buf);
+ sprintf(buf,"vXi-%d-%s",j,bufi);
+ grpnms[2*(i*md->nNHC+j)+1]=strdup(buf);
+ }
+ }
+ md->itcb=get_ebin_space(md->ebin,md->mdeb_n,
+ (const char **)grpnms,unit_invtime);
+ }
+ }
+ else
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ ni=groups->grps[egcTC].nm_ind[i];
+ bufi = *(groups->grpname[ni]);
+ sprintf(buf,"Xi-%s",bufi);
+ grpnms[2*i]=strdup(buf);
+ sprintf(buf,"vXi-%s",bufi);
+ grpnms[2*i+1]=strdup(buf);
+ }
+ md->itc=get_ebin_space(md->ebin,md->mde_n,
+ (const char **)grpnms,unit_invtime);
+ }
+ }
+ }
+ else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
+ md->etc == etcVRESCALE)
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ ni=groups->grps[egcTC].nm_ind[i];
+ sprintf(buf,"Lamb-%s",*(groups->grpname[ni]));
+ grpnms[i]=strdup(buf);
+ }
+ md->itc=get_ebin_space(md->ebin,md->mde_n,(const char **)grpnms,"");
+ }
+
+ sfree(grpnms);
+
+
+ md->nU=groups->grps[egcACC].nr;
+ if (md->nU > 1)
+ {
+ snew(grpnms,3*md->nU);
+ for(i=0; (i<md->nU); i++)
+ {
+ ni=groups->grps[egcACC].nm_ind[i];
+ sprintf(buf,"Ux-%s",*(groups->grpname[ni]));
+ grpnms[3*i+XX]=strdup(buf);
+ sprintf(buf,"Uy-%s",*(groups->grpname[ni]));
+ grpnms[3*i+YY]=strdup(buf);
+ sprintf(buf,"Uz-%s",*(groups->grpname[ni]));
+ grpnms[3*i+ZZ]=strdup(buf);
+ }
+ md->iu=get_ebin_space(md->ebin,3*md->nU,(const char **)grpnms,unit_vel);
+ sfree(grpnms);
+ }
+
+ if ( fp_ene )
+ {
+ do_enxnms(fp_ene,&md->ebin->nener,&md->ebin->enm);
+ }
+
+ md->print_grpnms=NULL;
+
+ /* check whether we're going to write dh histograms */
+ md->dhc=NULL;
+ if (ir->separate_dhdl_file == sepdhdlfileNO )
+ {
+ int i;
+ snew(md->dhc, 1);
+
+ mde_delta_h_coll_init(md->dhc, ir);
+ md->fp_dhdl = NULL;
+ }
+ else
+ {
+ md->fp_dhdl = fp_dhdl;
+ }
+ md->dhdl_derivatives = (ir->dhdl_derivatives==dhdlderivativesYES);
+ return md;
+}
+
+FILE *open_dhdl(const char *filename,const t_inputrec *ir,
+ const output_env_t oenv)
+{
+ FILE *fp;
+ const char *dhdl="dH/d\\lambda",*deltag="\\DeltaH",*lambda="\\lambda";
+ char title[STRLEN],label_x[STRLEN],label_y[STRLEN];
+ char **setname;
+ char buf[STRLEN];
+
+ sprintf(label_x,"%s (%s)","Time",unit_time);
+ if (ir->n_flambda == 0)
+ {
+ sprintf(title,"%s",dhdl);
+ sprintf(label_y,"%s (%s %s)",
+ dhdl,unit_energy,"[\\lambda]\\S-1\\N");
+ }
+ else
+ {
+ sprintf(title,"%s, %s",dhdl,deltag);
+ sprintf(label_y,"(%s)",unit_energy);
+ }
+ fp = gmx_fio_fopen(filename,"w+");
+ xvgr_header(fp,title,label_x,label_y,exvggtXNY,oenv);
+
+ if (ir->delta_lambda == 0)
+ {
+ sprintf(buf,"T = %g (K), %s = %g",
+ ir->opts.ref_t[0],lambda,ir->init_lambda);
+ }
+ else
+ {
+ sprintf(buf,"T = %g (K)",
+ ir->opts.ref_t[0]);
+ }
+ xvgr_subtitle(fp,buf,oenv);
+
+ if (ir->n_flambda > 0)
+ {
+ int nsets,s,nsi=0;
+ /* g_bar has to determine the lambda values used in this simulation
+ * from this xvg legend. */
+ nsets = ( (ir->dhdl_derivatives==dhdlderivativesYES) ? 1 : 0) +
+ ir->n_flambda;
+ snew(setname,nsets);
+ if (ir->dhdl_derivatives == dhdlderivativesYES)
+ {
+ sprintf(buf,"%s %s %g",dhdl,lambda,ir->init_lambda);
+ setname[nsi++] = strdup(buf);
+ }
+ for(s=0; s<ir->n_flambda; s++)
+ {
+ sprintf(buf,"%s %s %g",deltag,lambda,ir->flambda[s]);
+ setname[nsi++] = strdup(buf);
+ }
+ xvgr_legend(fp,nsets,(const char**)setname,oenv);
+
+ for(s=0; s<nsets; s++)
+ {
+ sfree(setname[s]);
+ }
+ sfree(setname);
+ }
+
+ return fp;
+}
+
+static void copy_energy(t_mdebin *md, real e[],real ecpy[])
+{
+ int i,j;
+
+ for(i=j=0; (i<F_NRE); i++)
+ if (md->bEner[i])
+ ecpy[j++] = e[i];
+ if (j != md->f_nre)
+ gmx_incons("Number of energy terms wrong");
+}
+
+void upd_mdebin(t_mdebin *md, gmx_bool write_dhdl,
+ gmx_bool bSum,
+ double time,
+ real tmass,
+ gmx_enerdata_t *enerd,
+ t_state *state,
+ matrix box,
+ tensor svir,
+ tensor fvir,
+ tensor vir,
+ tensor pres,
+ gmx_ekindata_t *ekind,
+ rvec mu_tot,
+ gmx_constr_t constr)
+{
+ int i,j,k,kk,m,n,gid;
+ real crmsd[2],tmp6[6];
+ real bs[NTRICLBOXS],vol,dens,pv,enthalpy;
+ real eee[egNR];
+ real ecopy[F_NRE];
+ real tmp;
+ gmx_bool bNoseHoover;
+
+ /* Do NOT use the box in the state variable, but the separate box provided
+ * as an argument. This is because we sometimes need to write the box from
+ * the last timestep to match the trajectory frames.
+ */
+ copy_energy(md, enerd->term,ecopy);
+ add_ebin(md->ebin,md->ie,md->f_nre,ecopy,bSum);
+ if (md->nCrmsd)
+ {
+ crmsd[0] = constr_rmsd(constr,FALSE);
+ if (md->nCrmsd > 1)
+ {
+ crmsd[1] = constr_rmsd(constr,TRUE);
+ }
+ add_ebin(md->ebin,md->iconrmsd,md->nCrmsd,crmsd,FALSE);
+ }
+ if (md->bDynBox)
+ {
+ int nboxs;
+ if(md->bTricl)
+ {
+ bs[0] = box[XX][XX];
+ bs[1] = box[YY][YY];
+ bs[2] = box[ZZ][ZZ];
+ bs[3] = box[YY][XX];
+ bs[4] = box[ZZ][XX];
+ bs[5] = box[ZZ][YY];
+ nboxs=NTRICLBOXS;
+ }
+ else
+ {
+ bs[0] = box[XX][XX];
+ bs[1] = box[YY][YY];
+ bs[2] = box[ZZ][ZZ];
+ nboxs=NBOXS;
+ }
+ vol = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
+ dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
+
+ /* This is pV (in kJ/mol). The pressure is the reference pressure,
+ not the instantaneous pressure */
+ pv = 0;
+ for (i=0;i<DIM;i++)
+ {
+ for (j=0;j<DIM;j++)
+ {
+ if (i>j)
+ {
+ pv += box[i][j]*md->ref_p[i][j]/PRESFAC;
+ }
+ else
+ {
+ pv += box[j][i]*md->ref_p[j][i]/PRESFAC;
+ }
+ }
+ }
+
+ add_ebin(md->ebin,md->ib ,nboxs,bs ,bSum);
+ add_ebin(md->ebin,md->ivol ,1 ,&vol ,bSum);
+ add_ebin(md->ebin,md->idens,1 ,&dens,bSum);
+ add_ebin(md->ebin,md->ipv ,1 ,&pv ,bSum);
+ enthalpy = pv + enerd->term[F_ETOT];
+ add_ebin(md->ebin,md->ienthalpy ,1 ,&enthalpy ,bSum);
+ }
+ if (md->bConstrVir)
+ {
+ add_ebin(md->ebin,md->isvir,9,svir[0],bSum);
+ add_ebin(md->ebin,md->ifvir,9,fvir[0],bSum);
+ }
+ add_ebin(md->ebin,md->ivir,9,vir[0],bSum);
+ add_ebin(md->ebin,md->ipres,9,pres[0],bSum);
+ tmp = (pres[ZZ][ZZ]-(pres[XX][XX]+pres[YY][YY])*0.5)*box[ZZ][ZZ];
+ add_ebin(md->ebin,md->isurft,1,&tmp,bSum);
+ if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
+ {
+ tmp6[0] = state->boxv[XX][XX];
+ tmp6[1] = state->boxv[YY][YY];
+ tmp6[2] = state->boxv[ZZ][ZZ];
+ tmp6[3] = state->boxv[YY][XX];
+ tmp6[4] = state->boxv[ZZ][XX];
+ tmp6[5] = state->boxv[ZZ][YY];
+ add_ebin(md->ebin,md->ipc,md->bTricl ? 6 : 3,tmp6,bSum);
+ }
+ add_ebin(md->ebin,md->imu,3,mu_tot,bSum);
+ if (ekind && ekind->cosacc.cos_accel != 0)
+ {
+ vol = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
+ dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
+ add_ebin(md->ebin,md->ivcos,1,&(ekind->cosacc.vcos),bSum);
+ /* 1/viscosity, unit 1/(kg m^-1 s^-1) */
+ tmp = 1/(ekind->cosacc.cos_accel/(ekind->cosacc.vcos*PICO)
+ *vol*sqr(box[ZZ][ZZ]*NANO/(2*M_PI)));
+ add_ebin(md->ebin,md->ivisc,1,&tmp,bSum);
+ }
+ if (md->nE > 1)
+ {
+ n=0;
+ for(i=0; (i<md->nEg); i++)
+ {
+ for(j=i; (j<md->nEg); j++)
+ {
+ gid=GID(i,j,md->nEg);
+ for(k=kk=0; (k<egNR); k++)
+ {
+ if (md->bEInd[k])
+ {
+ eee[kk++] = enerd->grpp.ener[k][gid];
+ }
+ }
+ add_ebin(md->ebin,md->igrp[n],md->nEc,eee,bSum);
+ n++;
+ }
+ }
+ }
+
+ if (ekind)
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ md->tmp_r[i] = ekind->tcstat[i].T;
+ }
+ add_ebin(md->ebin,md->itemp,md->nTC,md->tmp_r,bSum);
+
+ /* whether to print Nose-Hoover chains: */
+ bNoseHoover = (getenv("GMX_NOSEHOOVER_CHAINS") != NULL);
+
+ if (md->etc == etcNOSEHOOVER)
+ {
+ if (bNoseHoover)
+ {
+ if (md->bNHC_trotter)
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ for (j=0;j<md->nNHC;j++)
+ {
+ k = i*md->nNHC+j;
+ md->tmp_r[2*k] = state->nosehoover_xi[k];
+ md->tmp_r[2*k+1] = state->nosehoover_vxi[k];
+ }
+ }
+ add_ebin(md->ebin,md->itc,md->mde_n,md->tmp_r,bSum);
+
+ if (md->bMTTK) {
+ for(i=0; (i<md->nTCP); i++)
+ {
+ for (j=0;j<md->nNHC;j++)
+ {
+ k = i*md->nNHC+j;
+ md->tmp_r[2*k] = state->nhpres_xi[k];
+ md->tmp_r[2*k+1] = state->nhpres_vxi[k];
+ }
+ }
+ add_ebin(md->ebin,md->itcb,md->mdeb_n,md->tmp_r,bSum);
+ }
+
+ }
+ else
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ md->tmp_r[2*i] = state->nosehoover_xi[i];
+ md->tmp_r[2*i+1] = state->nosehoover_vxi[i];
+ }
+ add_ebin(md->ebin,md->itc,md->mde_n,md->tmp_r,bSum);
+ }
+ }
+ }
+ else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
+ md->etc == etcVRESCALE)
+ {
+ for(i=0; (i<md->nTC); i++)
+ {
+ md->tmp_r[i] = ekind->tcstat[i].lambda;
+ }
+ add_ebin(md->ebin,md->itc,md->nTC,md->tmp_r,bSum);
+ }
+ }
+
+ if (ekind && md->nU > 1)
+ {
+ for(i=0; (i<md->nU); i++)
+ {
+ copy_rvec(ekind->grpstat[i].u,md->tmp_v[i]);
+ }
+ add_ebin(md->ebin,md->iu,3*md->nU,md->tmp_v[0],bSum);
+ }
+
+ ebin_increase_count(md->ebin,bSum);
+
+ /* BAR + thermodynamic integration values */
+ if (write_dhdl)
+ {
+ if (md->fp_dhdl)
+ {
+ fprintf(md->fp_dhdl,"%.4f", time);
+
+ if (md->dhdl_derivatives)
+ {
+ fprintf(md->fp_dhdl," %g", enerd->term[F_DVDL]+
+ enerd->term[F_DKDL]+
+ enerd->term[F_DHDL_CON]);
+ }
+ for(i=1; i<enerd->n_lambda; i++)
+ {
+ fprintf(md->fp_dhdl," %g",
+ enerd->enerpart_lambda[i]-enerd->enerpart_lambda[0]);
+ }
+ fprintf(md->fp_dhdl,"\n");
+ }
+ /* and the binary BAR output */
+ if (md->dhc)
+ {
+ mde_delta_h_coll_add_dh(md->dhc,
+ enerd->term[F_DVDL]+ enerd->term[F_DKDL]+
+ enerd->term[F_DHDL_CON],
+ enerd->enerpart_lambda, time,
+ state->lambda);
+ }
+ }
+}
+
+void upd_mdebin_step(t_mdebin *md)
+{
+ ebin_increase_count(md->ebin,FALSE);
+}
+
+static void npr(FILE *log,int n,char c)
+{
+ for(; (n>0); n--) fprintf(log,"%c",c);
+}
+
+static void pprint(FILE *log,const char *s,t_mdebin *md)
+{
+ char CHAR='#';
+ int slen;
+ char buf1[22],buf2[22];
+
+ slen = strlen(s);
+ fprintf(log,"\t<====== ");
+ npr(log,slen,CHAR);
+ fprintf(log," ==>\n");
+ fprintf(log,"\t<==== %s ====>\n",s);
+ fprintf(log,"\t<== ");
+ npr(log,slen,CHAR);
+ fprintf(log," ======>\n\n");
+
+ fprintf(log,"\tStatistics over %s steps using %s frames\n",
+ gmx_step_str(md->ebin->nsteps_sim,buf1),
+ gmx_step_str(md->ebin->nsum_sim,buf2));
+ fprintf(log,"\n");
+}
+
+void print_ebin_header(FILE *log,gmx_large_int_t steps,double time,real lamb)
+{
+ char buf[22];
+
+ fprintf(log," %12s %12s %12s\n"
+ " %12s %12.5f %12.5f\n\n",
+ "Step","Time","Lambda",gmx_step_str(steps,buf),time,lamb);
+}
+
+void print_ebin(ener_file_t fp_ene,gmx_bool bEne,gmx_bool bDR,gmx_bool bOR,
+ FILE *log,
+ gmx_large_int_t step,double time,
+ int mode,gmx_bool bCompact,
+ t_mdebin *md,t_fcdata *fcd,
+ gmx_groups_t *groups,t_grpopts *opts)
+{
+ /*static char **grpnms=NULL;*/
+ char buf[246];
+ int i,j,n,ni,nj,ndr,nor,b;
+ int ndisre=0;
+ real *disre_rm3tav, *disre_rt;
+
+ /* these are for the old-style blocks (1 subblock, only reals), because
+ there can be only one per ID for these */
+ int nr[enxNR];
+ int id[enxNR];
+ real *block[enxNR];
+
+ /* temporary arrays for the lambda values to write out */
+ double enxlambda_data[2];
+
+ t_enxframe fr;
+
+ switch (mode)
+ {
+ case eprNORMAL:
+ init_enxframe(&fr);
+ fr.t = time;
+ fr.step = step;
+ fr.nsteps = md->ebin->nsteps;
+ fr.dt = md->delta_t;
+ fr.nsum = md->ebin->nsum;
+ fr.nre = (bEne) ? md->ebin->nener : 0;
+ fr.ener = md->ebin->e;
+ ndisre = bDR ? fcd->disres.npair : 0;
+ disre_rm3tav = fcd->disres.rm3tav;
+ disre_rt = fcd->disres.rt;
+ /* Optional additional old-style (real-only) blocks. */
+ for(i=0; i<enxNR; i++)
+ {
+ nr[i] = 0;
+ }
+ if (fcd->orires.nr > 0 && bOR)
+ {
+ diagonalize_orires_tensors(&(fcd->orires));
+ nr[enxOR] = fcd->orires.nr;
+ block[enxOR] = fcd->orires.otav;
+ id[enxOR] = enxOR;
+ nr[enxORI] = (fcd->orires.oinsl != fcd->orires.otav) ?
+ fcd->orires.nr : 0;
+ block[enxORI] = fcd->orires.oinsl;
+ id[enxORI] = enxORI;
+ nr[enxORT] = fcd->orires.nex*12;
+ block[enxORT] = fcd->orires.eig;
+ id[enxORT] = enxORT;
+ }
+
+ /* whether we are going to wrte anything out: */
+ if (fr.nre || ndisre || nr[enxOR] || nr[enxORI])
+ {
+
+ /* the old-style blocks go first */
+ fr.nblock = 0;
+ for(i=0; i<enxNR; i++)
+ {
+ if (nr[i] > 0)
+ {
+ fr.nblock = i + 1;
+ }
+ }
+ add_blocks_enxframe(&fr, fr.nblock);
+ for(b=0;b<fr.nblock;b++)
+ {
+ add_subblocks_enxblock(&(fr.block[b]), 1);
+ fr.block[b].id=id[b];
+ fr.block[b].sub[0].nr = nr[b];
+#ifndef GMX_DOUBLE
+ fr.block[b].sub[0].type = xdr_datatype_float;
+ fr.block[b].sub[0].fval = block[b];
+#else
+ fr.block[b].sub[0].type = xdr_datatype_double;
+ fr.block[b].sub[0].dval = block[b];
+#endif
+ }
+
+ /* check for disre block & fill it. */
+ if (ndisre>0)
+ {
+ int db = fr.nblock;
+ fr.nblock+=1;
+ add_blocks_enxframe(&fr, fr.nblock);
+
+ add_subblocks_enxblock(&(fr.block[db]), 2);
+ fr.block[db].id=enxDISRE;
+ fr.block[db].sub[0].nr=ndisre;
+ fr.block[db].sub[1].nr=ndisre;
+#ifndef GMX_DOUBLE
+ fr.block[db].sub[0].type=xdr_datatype_float;
+ fr.block[db].sub[1].type=xdr_datatype_float;
+ fr.block[db].sub[0].fval=disre_rt;
+ fr.block[db].sub[1].fval=disre_rm3tav;
+#else
+ fr.block[db].sub[0].type=xdr_datatype_double;
+ fr.block[db].sub[1].type=xdr_datatype_double;
+ fr.block[db].sub[0].dval=disre_rt;
+ fr.block[db].sub[1].dval=disre_rm3tav;
+#endif
+ }
+ /* here we can put new-style blocks */
+
+ /* Free energy perturbation blocks */
+ if (md->dhc)
+ {
+ mde_delta_h_coll_handle_block(md->dhc, &fr, fr.nblock);
+ }
+
+ /* do the actual I/O */
+ do_enx(fp_ene,&fr);
+ gmx_fio_check_file_position(enx_file_pointer(fp_ene));
+ if (fr.nre)
+ {
+ /* We have stored the sums, so reset the sum history */
+ reset_ebin_sums(md->ebin);
+ }
+
+ /* we can now free & reset the data in the blocks */
+ if (md->dhc)
+ mde_delta_h_coll_reset(md->dhc);
+ }
+ free_enxframe(&fr);
+ break;
+ case eprAVER:
+ if (log)
+ {
+ pprint(log,"A V E R A G E S",md);
+ }
+ break;
+ case eprRMS:
+ if (log)
+ {
+ pprint(log,"R M S - F L U C T U A T I O N S",md);
+ }
+ break;
+ default:
+ gmx_fatal(FARGS,"Invalid print mode (%d)",mode);
+ }
+
+ if (log)
+ {
+ for(i=0;i<opts->ngtc;i++)
+ {
+ if(opts->annealing[i]!=eannNO)
+ {
+ fprintf(log,"Current ref_t for group %s: %8.1f\n",
+ *(groups->grpname[groups->grps[egcTC].nm_ind[i]]),
+ opts->ref_t[i]);
+ }
+ }
+ if (mode==eprNORMAL && fcd->orires.nr>0)
+ {
+ print_orires_log(log,&(fcd->orires));
+ }
+ fprintf(log," Energies (%s)\n",unit_energy);
+ pr_ebin(log,md->ebin,md->ie,md->f_nre+md->nCrmsd,5,mode,TRUE);
+ fprintf(log,"\n");
+
+ if (!bCompact)
+ {
+ if (md->bDynBox)
+ {
+ pr_ebin(log,md->ebin,md->ib, md->bTricl ? NTRICLBOXS : NBOXS,5,
+ mode,TRUE);
+ fprintf(log,"\n");
+ }
+ if (md->bConstrVir)
+ {
+ fprintf(log," Constraint Virial (%s)\n",unit_energy);
+ pr_ebin(log,md->ebin,md->isvir,9,3,mode,FALSE);
+ fprintf(log,"\n");
+ fprintf(log," Force Virial (%s)\n",unit_energy);
+ pr_ebin(log,md->ebin,md->ifvir,9,3,mode,FALSE);
+ fprintf(log,"\n");
+ }
+ fprintf(log," Total Virial (%s)\n",unit_energy);
+ pr_ebin(log,md->ebin,md->ivir,9,3,mode,FALSE);
+ fprintf(log,"\n");
+ fprintf(log," Pressure (%s)\n",unit_pres_bar);
+ pr_ebin(log,md->ebin,md->ipres,9,3,mode,FALSE);
+ fprintf(log,"\n");
+ fprintf(log," Total Dipole (%s)\n",unit_dipole_D);
+ pr_ebin(log,md->ebin,md->imu,3,3,mode,FALSE);
+ fprintf(log,"\n");
+
+ if (md->nE > 1)
+ {
+ if (md->print_grpnms==NULL)
+ {
+ snew(md->print_grpnms,md->nE);
+ n=0;
+ for(i=0; (i<md->nEg); i++)
+ {
+ ni=groups->grps[egcENER].nm_ind[i];
+ for(j=i; (j<md->nEg); j++)
+ {
+ nj=groups->grps[egcENER].nm_ind[j];
+ sprintf(buf,"%s-%s",*(groups->grpname[ni]),
+ *(groups->grpname[nj]));
+ md->print_grpnms[n++]=strdup(buf);
+ }
+ }
+ }
+ sprintf(buf,"Epot (%s)",unit_energy);
+ fprintf(log,"%15s ",buf);
+ for(i=0; (i<egNR); i++)
+ {
+ if (md->bEInd[i])
+ {
+ fprintf(log,"%12s ",egrp_nm[i]);
+ }
+ }
+ fprintf(log,"\n");
+ for(i=0; (i<md->nE); i++)
+ {
+ fprintf(log,"%15s",md->print_grpnms[i]);
+ pr_ebin(log,md->ebin,md->igrp[i],md->nEc,md->nEc,mode,
+ FALSE);
+ }
+ fprintf(log,"\n");
+ }
+ if (md->nTC > 1)
+ {
+ pr_ebin(log,md->ebin,md->itemp,md->nTC,4,mode,TRUE);
+ fprintf(log,"\n");
+ }
+ if (md->nU > 1)
+ {
+ fprintf(log,"%15s %12s %12s %12s\n",
+ "Group","Ux","Uy","Uz");
+ for(i=0; (i<md->nU); i++)
+ {
+ ni=groups->grps[egcACC].nm_ind[i];
+ fprintf(log,"%15s",*groups->grpname[ni]);
+ pr_ebin(log,md->ebin,md->iu+3*i,3,3,mode,FALSE);
+ }
+ fprintf(log,"\n");
+ }
+ }
+ }
+
+}
+
+void update_energyhistory(energyhistory_t * enerhist,t_mdebin * mdebin)
+{
+ int i;
+
+ enerhist->nsteps = mdebin->ebin->nsteps;
+ enerhist->nsum = mdebin->ebin->nsum;
+ enerhist->nsteps_sim = mdebin->ebin->nsteps_sim;
+ enerhist->nsum_sim = mdebin->ebin->nsum_sim;
+ enerhist->nener = mdebin->ebin->nener;
+
+ if (mdebin->ebin->nsum > 0)
+ {
+ /* Check if we need to allocate first */
+ if(enerhist->ener_ave == NULL)
+ {
+ snew(enerhist->ener_ave,enerhist->nener);
+ snew(enerhist->ener_sum,enerhist->nener);
+ }
+
+ for(i=0;i<enerhist->nener;i++)
+ {
+ enerhist->ener_ave[i] = mdebin->ebin->e[i].eav;
+ enerhist->ener_sum[i] = mdebin->ebin->e[i].esum;
+ }
+ }
+
+ if (mdebin->ebin->nsum_sim > 0)
+ {
+ /* Check if we need to allocate first */
+ if(enerhist->ener_sum_sim == NULL)
+ {
+ snew(enerhist->ener_sum_sim,enerhist->nener);
+ }
+
+ for(i=0;i<enerhist->nener;i++)
+ {
+ enerhist->ener_sum_sim[i] = mdebin->ebin->e_sim[i].esum;
+ }
+ }
+ if (mdebin->dhc)
+ {
+ mde_delta_h_coll_update_energyhistory(mdebin->dhc, enerhist);
+ }
+}
+
+void restore_energyhistory_from_state(t_mdebin * mdebin,
+ energyhistory_t * enerhist)
+{
+ int i;
+
+ if ((enerhist->nsum > 0 || enerhist->nsum_sim > 0) &&
+ mdebin->ebin->nener != enerhist->nener)
+ {
+ gmx_fatal(FARGS,"Mismatch between number of energies in run input (%d) and checkpoint file (%d).",
+ mdebin->ebin->nener,enerhist->nener);
+ }
+
+ mdebin->ebin->nsteps = enerhist->nsteps;
+ mdebin->ebin->nsum = enerhist->nsum;
+ mdebin->ebin->nsteps_sim = enerhist->nsteps_sim;
+ mdebin->ebin->nsum_sim = enerhist->nsum_sim;
+
+ for(i=0; i<mdebin->ebin->nener; i++)
+ {
+ mdebin->ebin->e[i].eav =
+ (enerhist->nsum > 0 ? enerhist->ener_ave[i] : 0);
+ mdebin->ebin->e[i].esum =
+ (enerhist->nsum > 0 ? enerhist->ener_sum[i] : 0);
+ mdebin->ebin->e_sim[i].esum =
+ (enerhist->nsum_sim > 0 ? enerhist->ener_sum_sim[i] : 0);
+ }
+ if (mdebin->dhc)
+ {
+ mde_delta_h_coll_restore_energyhistory(mdebin->dhc, enerhist);
+ }
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include "sysstuff.h"
+#include "string2.h"
+#include "network.h"
+#include "confio.h"
+#include "copyrite.h"
+#include "smalloc.h"
+#include "nrnb.h"
+#include "main.h"
+#include "force.h"
+#include "macros.h"
+#include "random.h"
+#include "names.h"
+#include "gmx_fatal.h"
+#include "txtdump.h"
+#include "typedefs.h"
+#include "update.h"
+#include "constr.h"
+#include "vec.h"
+#include "statutil.h"
+#include "tgroup.h"
+#include "mdebin.h"
+#include "vsite.h"
+#include "force.h"
+#include "mdrun.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "trnio.h"
+#include "sparsematrix.h"
+#include "mtxio.h"
+#include "mdatoms.h"
+#include "ns.h"
+#include "gmx_wallcycle.h"
+#include "mtop_util.h"
+#include "gmxfio.h"
+#include "pme.h"
+#include "membed.h"
+
+typedef struct {
+ t_state s;
+ rvec *f;
+ real epot;
+ real fnorm;
+ real fmax;
+ int a_fmax;
+} em_state_t;
+
+static em_state_t *init_em_state()
+{
+ em_state_t *ems;
+
+ snew(ems,1);
+
+ return ems;
+}
+
+static void print_em_start(FILE *fplog,t_commrec *cr,gmx_runtime_t *runtime,
+ gmx_wallcycle_t wcycle,
+ const char *name)
+{
+ char buf[STRLEN];
+
+ runtime_start(runtime);
+
+ sprintf(buf,"Started %s",name);
+ print_date_and_time(fplog,cr->nodeid,buf,NULL);
+
+ wallcycle_start(wcycle,ewcRUN);
+}
+static void em_time_end(FILE *fplog,t_commrec *cr,gmx_runtime_t *runtime,
+ gmx_wallcycle_t wcycle)
+{
+ wallcycle_stop(wcycle,ewcRUN);
+
+ runtime_end(runtime);
+}
+
+static void sp_header(FILE *out,const char *minimizer,real ftol,int nsteps)
+{
+ fprintf(out,"\n");
+ fprintf(out,"%s:\n",minimizer);
+ fprintf(out," Tolerance (Fmax) = %12.5e\n",ftol);
+ fprintf(out," Number of steps = %12d\n",nsteps);
+}
+
+static void warn_step(FILE *fp,real ftol,gmx_bool bLastStep,gmx_bool bConstrain)
+{
+ if (bLastStep)
+ {
+ fprintf(fp,"\nReached the maximum number of steps before reaching Fmax < %g\n",ftol);
+ }
+ else
+ {
+ fprintf(fp,"\nStepsize too small, or no change in energy.\n"
+ "Converged to machine precision,\n"
+ "but not to the requested precision Fmax < %g\n",
+ ftol);
+ if (sizeof(real)<sizeof(double))
+ {
+ fprintf(fp,"\nDouble precision normally gives you higher accuracy.\n");
+ }
+ if (bConstrain)
+ {
+ fprintf(fp,"You might need to increase your constraint accuracy, or turn\n"
+ "off constraints alltogether (set constraints = none in mdp file)\n");
+ }
+ }
+}
+
+
+
+static void print_converged(FILE *fp,const char *alg,real ftol,
+ gmx_large_int_t count,gmx_bool bDone,gmx_large_int_t nsteps,
+ real epot,real fmax, int nfmax, real fnorm)
+{
+ char buf[STEPSTRSIZE];
+
+ if (bDone)
+ fprintf(fp,"\n%s converged to Fmax < %g in %s steps\n",
+ alg,ftol,gmx_step_str(count,buf));
+ else if(count<nsteps)
+ fprintf(fp,"\n%s converged to machine precision in %s steps,\n"
+ "but did not reach the requested Fmax < %g.\n",
+ alg,gmx_step_str(count,buf),ftol);
+ else
+ fprintf(fp,"\n%s did not converge to Fmax < %g in %s steps.\n",
+ alg,ftol,gmx_step_str(count,buf));
+
+#ifdef GMX_DOUBLE
+ fprintf(fp,"Potential Energy = %21.14e\n",epot);
+ fprintf(fp,"Maximum force = %21.14e on atom %d\n",fmax,nfmax+1);
+ fprintf(fp,"Norm of force = %21.14e\n",fnorm);
+#else
+ fprintf(fp,"Potential Energy = %14.7e\n",epot);
+ fprintf(fp,"Maximum force = %14.7e on atom %d\n",fmax,nfmax+1);
+ fprintf(fp,"Norm of force = %14.7e\n",fnorm);
+#endif
+}
+
+static void get_f_norm_max(t_commrec *cr,
+ t_grpopts *opts,t_mdatoms *mdatoms,rvec *f,
+ real *fnorm,real *fmax,int *a_fmax)
+{
+ double fnorm2,*sum;
+ real fmax2,fmax2_0,fam;
+ int la_max,a_max,start,end,i,m,gf;
+
+ /* This routine finds the largest force and returns it.
+ * On parallel machines the global max is taken.
+ */
+ fnorm2 = 0;
+ fmax2 = 0;
+ la_max = -1;
+ gf = 0;
+ start = mdatoms->start;
+ end = mdatoms->homenr + start;
+ if (mdatoms->cFREEZE) {
+ for(i=start; i<end; i++) {
+ gf = mdatoms->cFREEZE[i];
+ fam = 0;
+ for(m=0; m<DIM; m++)
+ if (!opts->nFreeze[gf][m])
+ fam += sqr(f[i][m]);
+ fnorm2 += fam;
+ if (fam > fmax2) {
+ fmax2 = fam;
+ la_max = i;
+ }
+ }
+ } else {
+ for(i=start; i<end; i++) {
+ fam = norm2(f[i]);
+ fnorm2 += fam;
+ if (fam > fmax2) {
+ fmax2 = fam;
+ la_max = i;
+ }
+ }
+ }
+
+ if (la_max >= 0 && DOMAINDECOMP(cr)) {
+ a_max = cr->dd->gatindex[la_max];
+ } else {
+ a_max = la_max;
+ }
+ if (PAR(cr)) {
+ snew(sum,2*cr->nnodes+1);
+ sum[2*cr->nodeid] = fmax2;
+ sum[2*cr->nodeid+1] = a_max;
+ sum[2*cr->nnodes] = fnorm2;
+ gmx_sumd(2*cr->nnodes+1,sum,cr);
+ fnorm2 = sum[2*cr->nnodes];
+ /* Determine the global maximum */
+ for(i=0; i<cr->nnodes; i++) {
+ if (sum[2*i] > fmax2) {
+ fmax2 = sum[2*i];
+ a_max = (int)(sum[2*i+1] + 0.5);
+ }
+ }
+ sfree(sum);
+ }
+
+ if (fnorm)
+ *fnorm = sqrt(fnorm2);
+ if (fmax)
+ *fmax = sqrt(fmax2);
+ if (a_fmax)
+ *a_fmax = a_max;
+}
+
+static void get_state_f_norm_max(t_commrec *cr,
+ t_grpopts *opts,t_mdatoms *mdatoms,
+ em_state_t *ems)
+{
+ get_f_norm_max(cr,opts,mdatoms,ems->f,&ems->fnorm,&ems->fmax,&ems->a_fmax);
+}
+
+void init_em(FILE *fplog,const char *title,
+ t_commrec *cr,t_inputrec *ir,
+ t_state *state_global,gmx_mtop_t *top_global,
+ em_state_t *ems,gmx_localtop_t **top,
+ rvec **f,rvec **f_global,
+ t_nrnb *nrnb,rvec mu_tot,
+ t_forcerec *fr,gmx_enerdata_t **enerd,
+ t_graph **graph,t_mdatoms *mdatoms,gmx_global_stat_t *gstat,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int nfile,const t_filenm fnm[],
+ gmx_mdoutf_t **outf,t_mdebin **mdebin)
+{
+ int start,homenr,i;
+ real dvdlambda;
+
+ if (fplog)
+ {
+ fprintf(fplog,"Initiating %s\n",title);
+ }
+
+ state_global->ngtc = 0;
+
+ /* Initiate some variables */
+ if (ir->efep != efepNO)
+ {
+ state_global->lambda = ir->init_lambda;
+ }
+ else
+ {
+ state_global->lambda = 0.0;
+ }
+
+ init_nrnb(nrnb);
+
+ if (DOMAINDECOMP(cr))
+ {
+ *top = dd_init_local_top(top_global);
+
+ dd_init_local_state(cr->dd,state_global,&ems->s);
+
+ *f = NULL;
+
+ /* Distribute the charge groups over the nodes from the master node */
+ dd_partition_system(fplog,ir->init_step,cr,TRUE,1,
+ state_global,top_global,ir,
+ &ems->s,&ems->f,mdatoms,*top,
+ fr,vsite,NULL,constr,
+ nrnb,NULL,FALSE);
+ dd_store_state(cr->dd,&ems->s);
+
+ if (ir->nstfout)
+ {
+ snew(*f_global,top_global->natoms);
+ }
+ else
+ {
+ *f_global = NULL;
+ }
+ *graph = NULL;
+ }
+ else
+ {
+ snew(*f,top_global->natoms);
+
+ /* Just copy the state */
+ ems->s = *state_global;
+ snew(ems->s.x,ems->s.nalloc);
+ snew(ems->f,ems->s.nalloc);
+ for(i=0; i<state_global->natoms; i++)
+ {
+ copy_rvec(state_global->x[i],ems->s.x[i]);
+ }
+ copy_mat(state_global->box,ems->s.box);
+
+ if (PAR(cr) && ir->eI != eiNM)
+ {
+ /* Initialize the particle decomposition and split the topology */
+ *top = split_system(fplog,top_global,ir,cr);
+
+ pd_cg_range(cr,&fr->cg0,&fr->hcg);
+ }
+ else
+ {
+ *top = gmx_mtop_generate_local_top(top_global,ir);
+ }
+ *f_global = *f;
+
+ if (ir->ePBC != epbcNONE && !ir->bPeriodicMols)
+ {
+ *graph = mk_graph(fplog,&((*top)->idef),0,top_global->natoms,FALSE,FALSE);
+ }
+ else
+ {
+ *graph = NULL;
+ }
+
+ if (PARTDECOMP(cr))
+ {
+ pd_at_range(cr,&start,&homenr);
+ homenr -= start;
+ }
+ else
+ {
+ start = 0;
+ homenr = top_global->natoms;
+ }
+ atoms2md(top_global,ir,0,NULL,start,homenr,mdatoms);
+ update_mdatoms(mdatoms,state_global->lambda);
+
+ if (vsite)
+ {
+ set_vsite_top(vsite,*top,mdatoms,cr);
+ }
+ }
+
+ if (constr)
+ {
+ if (ir->eConstrAlg == econtSHAKE &&
+ gmx_mtop_ftype_count(top_global,F_CONSTR) > 0)
+ {
+ gmx_fatal(FARGS,"Can not do energy minimization with %s, use %s\n",
+ econstr_names[econtSHAKE],econstr_names[econtLINCS]);
+ }
+
+ if (!DOMAINDECOMP(cr))
+ {
+ set_constraints(constr,*top,ir,mdatoms,cr);
+ }
+
+ if (!ir->bContinuation)
+ {
+ /* Constrain the starting coordinates */
+ dvdlambda=0;
+ constrain(PAR(cr) ? NULL : fplog,TRUE,TRUE,constr,&(*top)->idef,
+ ir,NULL,cr,-1,0,mdatoms,
+ ems->s.x,ems->s.x,NULL,ems->s.box,
+ ems->s.lambda,&dvdlambda,
+ NULL,NULL,nrnb,econqCoord,FALSE,0,0);
+ }
+ }
+
+ if (PAR(cr))
+ {
+ *gstat = global_stat_init(ir);
+ }
+
+ *outf = init_mdoutf(nfile,fnm,0,cr,ir,NULL);
+
+ snew(*enerd,1);
+ init_enerdata(top_global->groups.grps[egcENER].nr,ir->n_flambda,*enerd);
+
+ if (mdebin != NULL)
+ {
+ /* Init bin for energy stuff */
+ *mdebin = init_mdebin((*outf)->fp_ene,top_global,ir,NULL);
+ }
+
+ clear_rvec(mu_tot);
+ calc_shifts(ems->s.box,fr->shift_vec);
+}
+
+static void finish_em(FILE *fplog,t_commrec *cr,gmx_mdoutf_t *outf,
+ gmx_runtime_t *runtime,gmx_wallcycle_t wcycle)
+{
+ if (!(cr->duty & DUTY_PME)) {
+ /* Tell the PME only node to finish */
+ gmx_pme_finish(cr);
+ }
+
+ done_mdoutf(outf);
+
+ em_time_end(fplog,cr,runtime,wcycle);
+}
+
+static void swap_em_state(em_state_t *ems1,em_state_t *ems2)
+{
+ em_state_t tmp;
+
+ tmp = *ems1;
+ *ems1 = *ems2;
+ *ems2 = tmp;
+}
+
+static void copy_em_coords_back(em_state_t *ems,t_state *state,rvec *f)
+{
+ int i;
+
+ for(i=0; (i<state->natoms); i++)
+ copy_rvec(ems->s.x[i],state->x[i]);
+ if (f != NULL)
+ copy_rvec(ems->f[i],f[i]);
+}
+
+static void write_em_traj(FILE *fplog,t_commrec *cr,
+ gmx_mdoutf_t *outf,
+ gmx_bool bX,gmx_bool bF,const char *confout,
+ gmx_mtop_t *top_global,
+ t_inputrec *ir,gmx_large_int_t step,
+ em_state_t *state,
+ t_state *state_global,rvec *f_global)
+{
+ int mdof_flags;
+
+ if ((bX || bF || confout != NULL) && !DOMAINDECOMP(cr))
+ {
+ f_global = state->f;
+ copy_em_coords_back(state,state_global,bF ? f_global : NULL);
+ }
+
+ mdof_flags = 0;
+ if (bX) { mdof_flags |= MDOF_X; }
+ if (bF) { mdof_flags |= MDOF_F; }
+ write_traj(fplog,cr,outf,mdof_flags,
+ top_global,step,(double)step,
+ &state->s,state_global,state->f,f_global,NULL,NULL);
+
+ if (confout != NULL && MASTER(cr))
+ {
+ if (ir->ePBC != epbcNONE && !ir->bPeriodicMols && DOMAINDECOMP(cr))
+ {
+ /* Make molecules whole only for confout writing */
+ do_pbc_mtop(fplog,ir->ePBC,state_global->box,top_global,
+ state_global->x);
+ }
+
+ write_sto_conf_mtop(confout,
+ *top_global->name,top_global,
+ state_global->x,NULL,ir->ePBC,state_global->box);
+ }
+}
+
+static void do_em_step(t_commrec *cr,t_inputrec *ir,t_mdatoms *md,
+ em_state_t *ems1,real a,rvec *f,em_state_t *ems2,
+ gmx_constr_t constr,gmx_localtop_t *top,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_large_int_t count)
+
+{
+ t_state *s1,*s2;
+ int start,end,gf,i,m;
+ rvec *x1,*x2;
+ real dvdlambda;
+
+ s1 = &ems1->s;
+ s2 = &ems2->s;
+
+ if (DOMAINDECOMP(cr) && s1->ddp_count != cr->dd->ddp_count)
+ gmx_incons("state mismatch in do_em_step");
+
+ s2->flags = s1->flags;
+
+ if (s2->nalloc != s1->nalloc) {
+ s2->nalloc = s1->nalloc;
+ srenew(s2->x,s1->nalloc);
+ srenew(ems2->f, s1->nalloc);
+ if (s2->flags & (1<<estCGP))
+ srenew(s2->cg_p, s1->nalloc);
+ }
+
+ s2->natoms = s1->natoms;
+ s2->lambda = s1->lambda;
+ copy_mat(s1->box,s2->box);
+
+ start = md->start;
+ end = md->start + md->homenr;
+
+ x1 = s1->x;
+ x2 = s2->x;
+ gf = 0;
+ for(i=start; i<end; i++) {
+ if (md->cFREEZE)
+ gf = md->cFREEZE[i];
+ for(m=0; m<DIM; m++) {
+ if (ir->opts.nFreeze[gf][m])
+ x2[i][m] = x1[i][m];
+ else
+ x2[i][m] = x1[i][m] + a*f[i][m];
+ }
+ }
+
+ if (s2->flags & (1<<estCGP)) {
+ /* Copy the CG p vector */
+ x1 = s1->cg_p;
+ x2 = s2->cg_p;
+ for(i=start; i<end; i++)
+ copy_rvec(x1[i],x2[i]);
+ }
+
+ if (DOMAINDECOMP(cr)) {
+ s2->ddp_count = s1->ddp_count;
+ if (s2->cg_gl_nalloc < s1->cg_gl_nalloc) {
+ s2->cg_gl_nalloc = s1->cg_gl_nalloc;
+ srenew(s2->cg_gl,s2->cg_gl_nalloc);
+ }
+ s2->ncg_gl = s1->ncg_gl;
+ for(i=0; i<s2->ncg_gl; i++)
+ s2->cg_gl[i] = s1->cg_gl[i];
+ s2->ddp_count_cg_gl = s1->ddp_count_cg_gl;
+ }
+
+ if (constr) {
+ wallcycle_start(wcycle,ewcCONSTR);
+ dvdlambda = 0;
+ constrain(NULL,TRUE,TRUE,constr,&top->idef,
+ ir,NULL,cr,count,0,md,
+ s1->x,s2->x,NULL,s2->box,s2->lambda,
+ &dvdlambda,NULL,NULL,nrnb,econqCoord,FALSE,0,0);
+ wallcycle_stop(wcycle,ewcCONSTR);
+ }
+}
+
+static void do_x_step(t_commrec *cr,int n,rvec *x1,real a,rvec *f,rvec *x2)
+
+{
+ int start,end,i,m;
+
+ if (DOMAINDECOMP(cr)) {
+ start = 0;
+ end = cr->dd->nat_home;
+ } else if (PARTDECOMP(cr)) {
+ pd_at_range(cr,&start,&end);
+ } else {
+ start = 0;
+ end = n;
+ }
+
+ for(i=start; i<end; i++) {
+ for(m=0; m<DIM; m++) {
+ x2[i][m] = x1[i][m] + a*f[i][m];
+ }
+ }
+}
+
+static void do_x_sub(t_commrec *cr,int n,rvec *x1,rvec *x2,real a,rvec *f)
+
+{
+ int start,end,i,m;
+
+ if (DOMAINDECOMP(cr)) {
+ start = 0;
+ end = cr->dd->nat_home;
+ } else if (PARTDECOMP(cr)) {
+ pd_at_range(cr,&start,&end);
+ } else {
+ start = 0;
+ end = n;
+ }
+
+ for(i=start; i<end; i++) {
+ for(m=0; m<DIM; m++) {
+ f[i][m] = (x1[i][m] - x2[i][m])*a;
+ }
+ }
+}
+
+static void em_dd_partition_system(FILE *fplog,int step,t_commrec *cr,
+ gmx_mtop_t *top_global,t_inputrec *ir,
+ em_state_t *ems,gmx_localtop_t *top,
+ t_mdatoms *mdatoms,t_forcerec *fr,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle)
+{
+ /* Repartition the domain decomposition */
+ wallcycle_start(wcycle,ewcDOMDEC);
+ dd_partition_system(fplog,step,cr,FALSE,1,
+ NULL,top_global,ir,
+ &ems->s,&ems->f,
+ mdatoms,top,fr,vsite,NULL,constr,
+ nrnb,wcycle,FALSE);
+ dd_store_state(cr->dd,&ems->s);
+ wallcycle_stop(wcycle,ewcDOMDEC);
+}
+
+static void evaluate_energy(FILE *fplog,gmx_bool bVerbose,t_commrec *cr,
+ t_state *state_global,gmx_mtop_t *top_global,
+ em_state_t *ems,gmx_localtop_t *top,
+ t_inputrec *inputrec,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_global_stat_t gstat,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ t_fcdata *fcd,
+ t_graph *graph,t_mdatoms *mdatoms,
+ t_forcerec *fr,rvec mu_tot,
+ gmx_enerdata_t *enerd,tensor vir,tensor pres,
+ gmx_large_int_t count,gmx_bool bFirst)
+{
+ real t;
+ gmx_bool bNS;
+ int nabnsb;
+ tensor force_vir,shake_vir,ekin;
+ real dvdl,prescorr,enercorr,dvdlcorr;
+ real terminate=0;
+
+ /* Set the time to the initial time, the time does not change during EM */
+ t = inputrec->init_t;
+
+ if (bFirst ||
+ (DOMAINDECOMP(cr) && ems->s.ddp_count < cr->dd->ddp_count)) {
+ /* This the first state or an old state used before the last ns */
+ bNS = TRUE;
+ } else {
+ bNS = FALSE;
+ if (inputrec->nstlist > 0) {
+ bNS = TRUE;
+ } else if (inputrec->nstlist == -1) {
+ nabnsb = natoms_beyond_ns_buffer(inputrec,fr,&top->cgs,NULL,ems->s.x);
+ if (PAR(cr))
+ gmx_sumi(1,&nabnsb,cr);
+ bNS = (nabnsb > 0);
+ }
+ }
+
+ if (vsite)
+ construct_vsites(fplog,vsite,ems->s.x,nrnb,1,NULL,
+ top->idef.iparams,top->idef.il,
+ fr->ePBC,fr->bMolPBC,graph,cr,ems->s.box);
+
+ if (DOMAINDECOMP(cr)) {
+ if (bNS) {
+ /* Repartition the domain decomposition */
+ em_dd_partition_system(fplog,count,cr,top_global,inputrec,
+ ems,top,mdatoms,fr,vsite,constr,
+ nrnb,wcycle);
+ }
+ }
+
+ /* Calc force & energy on new trial position */
+ /* do_force always puts the charge groups in the box and shifts again
+ * We do not unshift, so molecules are always whole in congrad.c
+ */
+ do_force(fplog,cr,inputrec,
+ count,nrnb,wcycle,top,top_global,&top_global->groups,
+ ems->s.box,ems->s.x,&ems->s.hist,
+ ems->f,force_vir,mdatoms,enerd,fcd,
+ ems->s.lambda,graph,fr,vsite,mu_tot,t,NULL,NULL,TRUE,
+ GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | GMX_FORCE_VIRIAL |
+ (bNS ? GMX_FORCE_NS | GMX_FORCE_DOLR : 0));
+
+ /* Clear the unused shake virial and pressure */
+ clear_mat(shake_vir);
+ clear_mat(pres);
+
+ /* Calculate long range corrections to pressure and energy */
+ calc_dispcorr(fplog,inputrec,fr,count,top_global->natoms,ems->s.box,ems->s.lambda,
+ pres,force_vir,&prescorr,&enercorr,&dvdlcorr);
+ /* don't think these next 4 lines can be moved in for now, because we
+ don't always want to write it -- figure out how to clean this up MRS 8/4/2009 */
+ enerd->term[F_DISPCORR] = enercorr;
+ enerd->term[F_EPOT] += enercorr;
+ enerd->term[F_PRES] += prescorr;
+ enerd->term[F_DVDL] += dvdlcorr;
+
+ /* Communicate stuff when parallel */
+ if (PAR(cr) && inputrec->eI != eiNM)
+ {
+ wallcycle_start(wcycle,ewcMoveE);
+
+ global_stat(fplog,gstat,cr,enerd,force_vir,shake_vir,mu_tot,
+ inputrec,NULL,NULL,NULL,1,&terminate,
+ top_global,&ems->s,FALSE,
+ CGLO_ENERGY |
+ CGLO_PRESSURE |
+ CGLO_CONSTRAINT |
+ CGLO_FIRSTITERATE);
+
+ wallcycle_stop(wcycle,ewcMoveE);
+ }
+
+ ems->epot = enerd->term[F_EPOT];
+
+ if (constr) {
+ /* Project out the constraint components of the force */
+ wallcycle_start(wcycle,ewcCONSTR);
+ dvdl = 0;
+ constrain(NULL,FALSE,FALSE,constr,&top->idef,
+ inputrec,NULL,cr,count,0,mdatoms,
+ ems->s.x,ems->f,ems->f,ems->s.box,ems->s.lambda,&dvdl,
+ NULL,&shake_vir,nrnb,econqForceDispl,FALSE,0,0);
+ if (fr->bSepDVDL && fplog)
+ fprintf(fplog,sepdvdlformat,"Constraints",t,dvdl);
+ enerd->term[F_DHDL_CON] += dvdl;
+ m_add(force_vir,shake_vir,vir);
+ wallcycle_stop(wcycle,ewcCONSTR);
+ } else {
+ copy_mat(force_vir,vir);
+ }
+
+ clear_mat(ekin);
+ enerd->term[F_PRES] =
+ calc_pres(fr->ePBC,inputrec->nwall,ems->s.box,ekin,vir,pres,
+ (fr->eeltype==eelPPPM)?enerd->term[F_COUL_RECIP]:0.0);
+
+ sum_dhdl(enerd,ems->s.lambda,inputrec);
+
+ if (EI_ENERGY_MINIMIZATION(inputrec->eI))
+ {
+ get_state_f_norm_max(cr,&(inputrec->opts),mdatoms,ems);
+ }
+}
+
+static double reorder_partsum(t_commrec *cr,t_grpopts *opts,t_mdatoms *mdatoms,
+ gmx_mtop_t *mtop,
+ em_state_t *s_min,em_state_t *s_b)
+{
+ rvec *fm,*fb,*fmg;
+ t_block *cgs_gl;
+ int ncg,*cg_gl,*index,c,cg,i,a0,a1,a,gf,m;
+ double partsum;
+ unsigned char *grpnrFREEZE;
+
+ if (debug)
+ fprintf(debug,"Doing reorder_partsum\n");
+
+ fm = s_min->f;
+ fb = s_b->f;
+
+ cgs_gl = dd_charge_groups_global(cr->dd);
+ index = cgs_gl->index;
+
+ /* Collect fm in a global vector fmg.
+ * This conflicts with the spirit of domain decomposition,
+ * but to fully optimize this a much more complicated algorithm is required.
+ */
+ snew(fmg,mtop->natoms);
+
+ ncg = s_min->s.ncg_gl;
+ cg_gl = s_min->s.cg_gl;
+ i = 0;
+ for(c=0; c<ncg; c++) {
+ cg = cg_gl[c];
+ a0 = index[cg];
+ a1 = index[cg+1];
+ for(a=a0; a<a1; a++) {
+ copy_rvec(fm[i],fmg[a]);
+ i++;
+ }
+ }
+ gmx_sum(mtop->natoms*3,fmg[0],cr);
+
+ /* Now we will determine the part of the sum for the cgs in state s_b */
+ ncg = s_b->s.ncg_gl;
+ cg_gl = s_b->s.cg_gl;
+ partsum = 0;
+ i = 0;
+ gf = 0;
+ grpnrFREEZE = mtop->groups.grpnr[egcFREEZE];
+ for(c=0; c<ncg; c++) {
+ cg = cg_gl[c];
+ a0 = index[cg];
+ a1 = index[cg+1];
+ for(a=a0; a<a1; a++) {
+ if (mdatoms->cFREEZE && grpnrFREEZE) {
+ gf = grpnrFREEZE[i];
+ }
+ for(m=0; m<DIM; m++) {
+ if (!opts->nFreeze[gf][m]) {
+ partsum += (fb[i][m] - fmg[a][m])*fb[i][m];
+ }
+ }
+ i++;
+ }
+ }
+
+ sfree(fmg);
+
+ return partsum;
+}
+
+static real pr_beta(t_commrec *cr,t_grpopts *opts,t_mdatoms *mdatoms,
+ gmx_mtop_t *mtop,
+ em_state_t *s_min,em_state_t *s_b)
+{
+ rvec *fm,*fb;
+ double sum;
+ int gf,i,m;
+
+ /* This is just the classical Polak-Ribiere calculation of beta;
+ * it looks a bit complicated since we take freeze groups into account,
+ * and might have to sum it in parallel runs.
+ */
+
+ if (!DOMAINDECOMP(cr) ||
+ (s_min->s.ddp_count == cr->dd->ddp_count &&
+ s_b->s.ddp_count == cr->dd->ddp_count)) {
+ fm = s_min->f;
+ fb = s_b->f;
+ sum = 0;
+ gf = 0;
+ /* This part of code can be incorrect with DD,
+ * since the atom ordering in s_b and s_min might differ.
+ */
+ for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
+ if (mdatoms->cFREEZE)
+ gf = mdatoms->cFREEZE[i];
+ for(m=0; m<DIM; m++)
+ if (!opts->nFreeze[gf][m]) {
+ sum += (fb[i][m] - fm[i][m])*fb[i][m];
+ }
+ }
+ } else {
+ /* We need to reorder cgs while summing */
+ sum = reorder_partsum(cr,opts,mdatoms,mtop,s_min,s_b);
+ }
+ if (PAR(cr))
+ gmx_sumd(1,&sum,cr);
+
+ return sum/sqr(s_min->fnorm);
+}
+
+double do_cg(FILE *fplog,t_commrec *cr,
+ int nfile,const t_filenm fnm[],
+ const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
+ int nstglobalcomm,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int stepout,
+ t_inputrec *inputrec,
+ gmx_mtop_t *top_global,t_fcdata *fcd,
+ t_state *state_global,
+ t_mdatoms *mdatoms,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_edsam_t ed,
+ t_forcerec *fr,
+ int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ const char *CG="Polak-Ribiere Conjugate Gradients";
+
+ em_state_t *s_min,*s_a,*s_b,*s_c;
+ gmx_localtop_t *top;
+ gmx_enerdata_t *enerd;
+ rvec *f;
+ gmx_global_stat_t gstat;
+ t_graph *graph;
+ rvec *f_global,*p,*sf,*sfm;
+ double gpa,gpb,gpc,tmp,sum[2],minstep;
+ real fnormn;
+ real stepsize;
+ real a,b,c,beta=0.0;
+ real epot_repl=0;
+ real pnorm;
+ t_mdebin *mdebin;
+ gmx_bool converged,foundlower;
+ rvec mu_tot;
+ gmx_bool do_log=FALSE,do_ene=FALSE,do_x,do_f;
+ tensor vir,pres;
+ int number_steps,neval=0,nstcg=inputrec->nstcgsteep;
+ gmx_mdoutf_t *outf;
+ int i,m,gf,step,nminstep;
+ real terminate=0;
+
+ step=0;
+
+ s_min = init_em_state();
+ s_a = init_em_state();
+ s_b = init_em_state();
+ s_c = init_em_state();
+
+ /* Init em and store the local state in s_min */
+ init_em(fplog,CG,cr,inputrec,
+ state_global,top_global,s_min,&top,&f,&f_global,
+ nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
+ nfile,fnm,&outf,&mdebin);
+
+ /* Print to log file */
+ print_em_start(fplog,cr,runtime,wcycle,CG);
+
+ /* Max number of steps */
+ number_steps=inputrec->nsteps;
+
+ if (MASTER(cr))
+ sp_header(stderr,CG,inputrec->em_tol,number_steps);
+ if (fplog)
+ sp_header(fplog,CG,inputrec->em_tol,number_steps);
+
+ /* Call the force routine and some auxiliary (neighboursearching etc.) */
+ /* do_force always puts the charge groups in the box and shifts again
+ * We do not unshift, so molecules are always whole in congrad.c
+ */
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,s_min,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,-1,TRUE);
+ where();
+
+ if (MASTER(cr)) {
+ /* Copy stuff to the energy bin for easy printing etc. */
+ upd_mdebin(mdebin,FALSE,FALSE,(double)step,
+ mdatoms->tmass,enerd,&s_min->s,s_min->s.box,
+ NULL,NULL,vir,pres,NULL,mu_tot,constr);
+
+ print_ebin_header(fplog,step,step,s_min->s.lambda);
+ print_ebin(outf->fp_ene,TRUE,FALSE,FALSE,fplog,step,step,eprNORMAL,
+ TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+ }
+ where();
+
+ /* Estimate/guess the initial stepsize */
+ stepsize = inputrec->em_stepsize/s_min->fnorm;
+
+ if (MASTER(cr)) {
+ fprintf(stderr," F-max = %12.5e on atom %d\n",
+ s_min->fmax,s_min->a_fmax+1);
+ fprintf(stderr," F-Norm = %12.5e\n",
+ s_min->fnorm/sqrt(state_global->natoms));
+ fprintf(stderr,"\n");
+ /* and copy to the log file too... */
+ fprintf(fplog," F-max = %12.5e on atom %d\n",
+ s_min->fmax,s_min->a_fmax+1);
+ fprintf(fplog," F-Norm = %12.5e\n",
+ s_min->fnorm/sqrt(state_global->natoms));
+ fprintf(fplog,"\n");
+ }
+ /* Start the loop over CG steps.
+ * Each successful step is counted, and we continue until
+ * we either converge or reach the max number of steps.
+ */
+ converged = FALSE;
+ for(step=0; (number_steps<0 || (number_steps>=0 && step<=number_steps)) && !converged;step++) {
+
+ /* start taking steps in a new direction
+ * First time we enter the routine, beta=0, and the direction is
+ * simply the negative gradient.
+ */
+
+ /* Calculate the new direction in p, and the gradient in this direction, gpa */
+ p = s_min->s.cg_p;
+ sf = s_min->f;
+ gpa = 0;
+ gf = 0;
+ for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
+ if (mdatoms->cFREEZE)
+ gf = mdatoms->cFREEZE[i];
+ for(m=0; m<DIM; m++) {
+ if (!inputrec->opts.nFreeze[gf][m]) {
+ p[i][m] = sf[i][m] + beta*p[i][m];
+ gpa -= p[i][m]*sf[i][m];
+ /* f is negative gradient, thus the sign */
+ } else {
+ p[i][m] = 0;
+ }
+ }
+ }
+
+ /* Sum the gradient along the line across CPUs */
+ if (PAR(cr))
+ gmx_sumd(1,&gpa,cr);
+
+ /* Calculate the norm of the search vector */
+ get_f_norm_max(cr,&(inputrec->opts),mdatoms,p,&pnorm,NULL,NULL);
+
+ /* Just in case stepsize reaches zero due to numerical precision... */
+ if(stepsize<=0)
+ stepsize = inputrec->em_stepsize/pnorm;
+
+ /*
+ * Double check the value of the derivative in the search direction.
+ * If it is positive it must be due to the old information in the
+ * CG formula, so just remove that and start over with beta=0.
+ * This corresponds to a steepest descent step.
+ */
+ if(gpa>0) {
+ beta = 0;
+ step--; /* Don't count this step since we are restarting */
+ continue; /* Go back to the beginning of the big for-loop */
+ }
+
+ /* Calculate minimum allowed stepsize, before the average (norm)
+ * relative change in coordinate is smaller than precision
+ */
+ minstep=0;
+ for (i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
+ for(m=0; m<DIM; m++) {
+ tmp = fabs(s_min->s.x[i][m]);
+ if(tmp < 1.0)
+ tmp = 1.0;
+ tmp = p[i][m]/tmp;
+ minstep += tmp*tmp;
+ }
+ }
+ /* Add up from all CPUs */
+ if(PAR(cr))
+ gmx_sumd(1,&minstep,cr);
+
+ minstep = GMX_REAL_EPS/sqrt(minstep/(3*state_global->natoms));
+
+ if(stepsize<minstep) {
+ converged=TRUE;
+ break;
+ }
+
+ /* Write coordinates if necessary */
+ do_x = do_per_step(step,inputrec->nstxout);
+ do_f = do_per_step(step,inputrec->nstfout);
+
+ write_em_traj(fplog,cr,outf,do_x,do_f,NULL,
+ top_global,inputrec,step,
+ s_min,state_global,f_global);
+
+ /* Take a step downhill.
+ * In theory, we should minimize the function along this direction.
+ * That is quite possible, but it turns out to take 5-10 function evaluations
+ * for each line. However, we dont really need to find the exact minimum -
+ * it is much better to start a new CG step in a modified direction as soon
+ * as we are close to it. This will save a lot of energy evaluations.
+ *
+ * In practice, we just try to take a single step.
+ * If it worked (i.e. lowered the energy), we increase the stepsize but
+ * the continue straight to the next CG step without trying to find any minimum.
+ * If it didn't work (higher energy), there must be a minimum somewhere between
+ * the old position and the new one.
+ *
+ * Due to the finite numerical accuracy, it turns out that it is a good idea
+ * to even accept a SMALL increase in energy, if the derivative is still downhill.
+ * This leads to lower final energies in the tests I've done. / Erik
+ */
+ s_a->epot = s_min->epot;
+ a = 0.0;
+ c = a + stepsize; /* reference position along line is zero */
+
+ if (DOMAINDECOMP(cr) && s_min->s.ddp_count < cr->dd->ddp_count) {
+ em_dd_partition_system(fplog,step,cr,top_global,inputrec,
+ s_min,top,mdatoms,fr,vsite,constr,
+ nrnb,wcycle);
+ }
+
+ /* Take a trial step (new coords in s_c) */
+ do_em_step(cr,inputrec,mdatoms,s_min,c,s_min->s.cg_p,s_c,
+ constr,top,nrnb,wcycle,-1);
+
+ neval++;
+ /* Calculate energy for the trial step */
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,s_c,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,-1,FALSE);
+
+ /* Calc derivative along line */
+ p = s_c->s.cg_p;
+ sf = s_c->f;
+ gpc=0;
+ for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
+ for(m=0; m<DIM; m++)
+ gpc -= p[i][m]*sf[i][m]; /* f is negative gradient, thus the sign */
+ }
+ /* Sum the gradient along the line across CPUs */
+ if (PAR(cr))
+ gmx_sumd(1,&gpc,cr);
+
+ /* This is the max amount of increase in energy we tolerate */
+ tmp=sqrt(GMX_REAL_EPS)*fabs(s_a->epot);
+
+ /* Accept the step if the energy is lower, or if it is not significantly higher
+ * and the line derivative is still negative.
+ */
+ if (s_c->epot < s_a->epot || (gpc < 0 && s_c->epot < (s_a->epot + tmp))) {
+ foundlower = TRUE;
+ /* Great, we found a better energy. Increase step for next iteration
+ * if we are still going down, decrease it otherwise
+ */
+ if(gpc<0)
+ stepsize *= 1.618034; /* The golden section */
+ else
+ stepsize *= 0.618034; /* 1/golden section */
+ } else {
+ /* New energy is the same or higher. We will have to do some work
+ * to find a smaller value in the interval. Take smaller step next time!
+ */
+ foundlower = FALSE;
+ stepsize *= 0.618034;
+ }
+
+
+
+
+ /* OK, if we didn't find a lower value we will have to locate one now - there must
+ * be one in the interval [a=0,c].
+ * The same thing is valid here, though: Don't spend dozens of iterations to find
+ * the line minimum. We try to interpolate based on the derivative at the endpoints,
+ * and only continue until we find a lower value. In most cases this means 1-2 iterations.
+ *
+ * I also have a safeguard for potentially really patological functions so we never
+ * take more than 20 steps before we give up ...
+ *
+ * If we already found a lower value we just skip this step and continue to the update.
+ */
+ if (!foundlower) {
+ nminstep=0;
+
+ do {
+ /* Select a new trial point.
+ * If the derivatives at points a & c have different sign we interpolate to zero,
+ * otherwise just do a bisection.
+ */
+ if(gpa<0 && gpc>0)
+ b = a + gpa*(a-c)/(gpc-gpa);
+ else
+ b = 0.5*(a+c);
+
+ /* safeguard if interpolation close to machine accuracy causes errors:
+ * never go outside the interval
+ */
+ if(b<=a || b>=c)
+ b = 0.5*(a+c);
+
+ if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count) {
+ /* Reload the old state */
+ em_dd_partition_system(fplog,-1,cr,top_global,inputrec,
+ s_min,top,mdatoms,fr,vsite,constr,
+ nrnb,wcycle);
+ }
+
+ /* Take a trial step to this new point - new coords in s_b */
+ do_em_step(cr,inputrec,mdatoms,s_min,b,s_min->s.cg_p,s_b,
+ constr,top,nrnb,wcycle,-1);
+
+ neval++;
+ /* Calculate energy for the trial step */
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,s_b,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,-1,FALSE);
+
+ /* p does not change within a step, but since the domain decomposition
+ * might change, we have to use cg_p of s_b here.
+ */
+ p = s_b->s.cg_p;
+ sf = s_b->f;
+ gpb=0;
+ for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
+ for(m=0; m<DIM; m++)
+ gpb -= p[i][m]*sf[i][m]; /* f is negative gradient, thus the sign */
+ }
+ /* Sum the gradient along the line across CPUs */
+ if (PAR(cr))
+ gmx_sumd(1,&gpb,cr);
+
+ if (debug)
+ fprintf(debug,"CGE: EpotA %f EpotB %f EpotC %f gpb %f\n",
+ s_a->epot,s_b->epot,s_c->epot,gpb);
+
+ epot_repl = s_b->epot;
+
+ /* Keep one of the intervals based on the value of the derivative at the new point */
+ if (gpb > 0) {
+ /* Replace c endpoint with b */
+ swap_em_state(s_b,s_c);
+ c = b;
+ gpc = gpb;
+ } else {
+ /* Replace a endpoint with b */
+ swap_em_state(s_b,s_a);
+ a = b;
+ gpa = gpb;
+ }
+
+ /*
+ * Stop search as soon as we find a value smaller than the endpoints.
+ * Never run more than 20 steps, no matter what.
+ */
+ nminstep++;
+ } while ((epot_repl > s_a->epot || epot_repl > s_c->epot) &&
+ (nminstep < 20));
+
+ if (fabs(epot_repl - s_min->epot) < fabs(s_min->epot)*GMX_REAL_EPS ||
+ nminstep >= 20) {
+ /* OK. We couldn't find a significantly lower energy.
+ * If beta==0 this was steepest descent, and then we give up.
+ * If not, set beta=0 and restart with steepest descent before quitting.
+ */
+ if (beta == 0.0) {
+ /* Converged */
+ converged = TRUE;
+ break;
+ } else {
+ /* Reset memory before giving up */
+ beta = 0.0;
+ continue;
+ }
+ }
+
+ /* Select min energy state of A & C, put the best in B.
+ */
+ if (s_c->epot < s_a->epot) {
+ if (debug)
+ fprintf(debug,"CGE: C (%f) is lower than A (%f), moving C to B\n",
+ s_c->epot,s_a->epot);
+ swap_em_state(s_b,s_c);
+ gpb = gpc;
+ b = c;
+ } else {
+ if (debug)
+ fprintf(debug,"CGE: A (%f) is lower than C (%f), moving A to B\n",
+ s_a->epot,s_c->epot);
+ swap_em_state(s_b,s_a);
+ gpb = gpa;
+ b = a;
+ }
+
+ } else {
+ if (debug)
+ fprintf(debug,"CGE: Found a lower energy %f, moving C to B\n",
+ s_c->epot);
+ swap_em_state(s_b,s_c);
+ gpb = gpc;
+ b = c;
+ }
+
+ /* new search direction */
+ /* beta = 0 means forget all memory and restart with steepest descents. */
+ if (nstcg && ((step % nstcg)==0))
+ beta = 0.0;
+ else {
+ /* s_min->fnorm cannot be zero, because then we would have converged
+ * and broken out.
+ */
+
+ /* Polak-Ribiere update.
+ * Change to fnorm2/fnorm2_old for Fletcher-Reeves
+ */
+ beta = pr_beta(cr,&inputrec->opts,mdatoms,top_global,s_min,s_b);
+ }
+ /* Limit beta to prevent oscillations */
+ if (fabs(beta) > 5.0)
+ beta = 0.0;
+
+
+ /* update positions */
+ swap_em_state(s_min,s_b);
+ gpa = gpb;
+
+ /* Print it if necessary */
+ if (MASTER(cr)) {
+ if(bVerbose)
+ fprintf(stderr,"\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
+ step,s_min->epot,s_min->fnorm/sqrt(state_global->natoms),
+ s_min->fmax,s_min->a_fmax+1);
+ /* Store the new (lower) energies */
+ upd_mdebin(mdebin,FALSE,FALSE,(double)step,
+ mdatoms->tmass,enerd,&s_min->s,s_min->s.box,
+ NULL,NULL,vir,pres,NULL,mu_tot,constr);
+ do_log = do_per_step(step,inputrec->nstlog);
+ do_ene = do_per_step(step,inputrec->nstenergy);
+ if(do_log)
+ print_ebin_header(fplog,step,step,s_min->s.lambda);
+ print_ebin(outf->fp_ene,do_ene,FALSE,FALSE,
+ do_log ? fplog : NULL,step,step,eprNORMAL,
+ TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+ }
+
+ /* Stop when the maximum force lies below tolerance.
+ * If we have reached machine precision, converged is already set to true.
+ */
+ converged = converged || (s_min->fmax < inputrec->em_tol);
+
+ } /* End of the loop */
+
+ if (converged)
+ step--; /* we never took that last step in this case */
+
+ if (s_min->fmax > inputrec->em_tol)
+ {
+ if (MASTER(cr))
+ {
+ warn_step(stderr,inputrec->em_tol,step-1==number_steps,FALSE);
+ warn_step(fplog ,inputrec->em_tol,step-1==number_steps,FALSE);
+ }
+ converged = FALSE;
+ }
+
+ if (MASTER(cr)) {
+ /* If we printed energy and/or logfile last step (which was the last step)
+ * we don't have to do it again, but otherwise print the final values.
+ */
+ if(!do_log) {
+ /* Write final value to log since we didn't do anything the last step */
+ print_ebin_header(fplog,step,step,s_min->s.lambda);
+ }
+ if (!do_ene || !do_log) {
+ /* Write final energy file entries */
+ print_ebin(outf->fp_ene,!do_ene,FALSE,FALSE,
+ !do_log ? fplog : NULL,step,step,eprNORMAL,
+ TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+ }
+ }
+
+ /* Print some stuff... */
+ if (MASTER(cr))
+ fprintf(stderr,"\nwriting lowest energy coordinates.\n");
+
+ /* IMPORTANT!
+ * For accurate normal mode calculation it is imperative that we
+ * store the last conformation into the full precision binary trajectory.
+ *
+ * However, we should only do it if we did NOT already write this step
+ * above (which we did if do_x or do_f was true).
+ */
+ do_x = !do_per_step(step,inputrec->nstxout);
+ do_f = (inputrec->nstfout > 0 && !do_per_step(step,inputrec->nstfout));
+
+ write_em_traj(fplog,cr,outf,do_x,do_f,ftp2fn(efSTO,nfile,fnm),
+ top_global,inputrec,step,
+ s_min,state_global,f_global);
+
+ fnormn = s_min->fnorm/sqrt(state_global->natoms);
+
+ if (MASTER(cr)) {
+ print_converged(stderr,CG,inputrec->em_tol,step,converged,number_steps,
+ s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
+ print_converged(fplog,CG,inputrec->em_tol,step,converged,number_steps,
+ s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
+
+ fprintf(fplog,"\nPerformed %d energy evaluations in total.\n",neval);
+ }
+
+ finish_em(fplog,cr,outf,runtime,wcycle);
+
+ /* To print the actual number of steps we needed somewhere */
+ runtime->nsteps_done = step;
+
+ return 0;
+} /* That's all folks */
+
+
+double do_lbfgs(FILE *fplog,t_commrec *cr,
+ int nfile,const t_filenm fnm[],
+ const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
+ int nstglobalcomm,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int stepout,
+ t_inputrec *inputrec,
+ gmx_mtop_t *top_global,t_fcdata *fcd,
+ t_state *state,
+ t_mdatoms *mdatoms,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_edsam_t ed,
+ t_forcerec *fr,
+ int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ static const char *LBFGS="Low-Memory BFGS Minimizer";
+ em_state_t ems;
+ gmx_localtop_t *top;
+ gmx_enerdata_t *enerd;
+ rvec *f;
+ gmx_global_stat_t gstat;
+ t_graph *graph;
+ rvec *f_global;
+ int ncorr,nmaxcorr,point,cp,neval,nminstep;
+ double stepsize,gpa,gpb,gpc,tmp,minstep;
+ real *rho,*alpha,*ff,*xx,*p,*s,*lastx,*lastf,**dx,**dg;
+ real *xa,*xb,*xc,*fa,*fb,*fc,*xtmp,*ftmp;
+ real a,b,c,maxdelta,delta;
+ real diag,Epot0,Epot,EpotA,EpotB,EpotC;
+ real dgdx,dgdg,sq,yr,beta;
+ t_mdebin *mdebin;
+ gmx_bool converged,first;
+ rvec mu_tot;
+ real fnorm,fmax;
+ gmx_bool do_log,do_ene,do_x,do_f,foundlower,*frozen;
+ tensor vir,pres;
+ int start,end,number_steps;
+ gmx_mdoutf_t *outf;
+ int i,k,m,n,nfmax,gf,step;
+ /* not used */
+ real terminate;
+
+ if (PAR(cr))
+ gmx_fatal(FARGS,"Cannot do parallel L-BFGS Minimization - yet.\n");
+
+ n = 3*state->natoms;
+ nmaxcorr = inputrec->nbfgscorr;
+
+ /* Allocate memory */
+ /* Use pointers to real so we dont have to loop over both atoms and
+ * dimensions all the time...
+ * x/f are allocated as rvec *, so make new x0/f0 pointers-to-real
+ * that point to the same memory.
+ */
+ snew(xa,n);
+ snew(xb,n);
+ snew(xc,n);
+ snew(fa,n);
+ snew(fb,n);
+ snew(fc,n);
+ snew(frozen,n);
+
+ snew(p,n);
+ snew(lastx,n);
+ snew(lastf,n);
+ snew(rho,nmaxcorr);
+ snew(alpha,nmaxcorr);
+
+ snew(dx,nmaxcorr);
+ for(i=0;i<nmaxcorr;i++)
+ snew(dx[i],n);
+
+ snew(dg,nmaxcorr);
+ for(i=0;i<nmaxcorr;i++)
+ snew(dg[i],n);
+
+ step = 0;
+ neval = 0;
+
+ /* Init em */
+ init_em(fplog,LBFGS,cr,inputrec,
+ state,top_global,&ems,&top,&f,&f_global,
+ nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
+ nfile,fnm,&outf,&mdebin);
+ /* Do_lbfgs is not completely updated like do_steep and do_cg,
+ * so we free some memory again.
+ */
+ sfree(ems.s.x);
+ sfree(ems.f);
+
+ xx = (real *)state->x;
+ ff = (real *)f;
+
+ start = mdatoms->start;
+ end = mdatoms->homenr + start;
+
+ /* Print to log file */
+ print_em_start(fplog,cr,runtime,wcycle,LBFGS);
+
+ do_log = do_ene = do_x = do_f = TRUE;
+
+ /* Max number of steps */
+ number_steps=inputrec->nsteps;
+
+ /* Create a 3*natoms index to tell whether each degree of freedom is frozen */
+ gf = 0;
+ for(i=start; i<end; i++) {
+ if (mdatoms->cFREEZE)
+ gf = mdatoms->cFREEZE[i];
+ for(m=0; m<DIM; m++)
+ frozen[3*i+m]=inputrec->opts.nFreeze[gf][m];
+ }
+ if (MASTER(cr))
+ sp_header(stderr,LBFGS,inputrec->em_tol,number_steps);
+ if (fplog)
+ sp_header(fplog,LBFGS,inputrec->em_tol,number_steps);
+
+ if (vsite)
+ construct_vsites(fplog,vsite,state->x,nrnb,1,NULL,
+ top->idef.iparams,top->idef.il,
+ fr->ePBC,fr->bMolPBC,graph,cr,state->box);
+
+ /* Call the force routine and some auxiliary (neighboursearching etc.) */
+ /* do_force always puts the charge groups in the box and shifts again
+ * We do not unshift, so molecules are always whole
+ */
+ neval++;
+ ems.s.x = state->x;
+ ems.f = f;
+ evaluate_energy(fplog,bVerbose,cr,
+ state,top_global,&ems,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,-1,TRUE);
+ where();
+
+ if (MASTER(cr)) {
+ /* Copy stuff to the energy bin for easy printing etc. */
+ upd_mdebin(mdebin,FALSE,FALSE,(double)step,
+ mdatoms->tmass,enerd,state,state->box,
+ NULL,NULL,vir,pres,NULL,mu_tot,constr);
+
+ print_ebin_header(fplog,step,step,state->lambda);
+ print_ebin(outf->fp_ene,TRUE,FALSE,FALSE,fplog,step,step,eprNORMAL,
+ TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+ }
+ where();
+
+ /* This is the starting energy */
+ Epot = enerd->term[F_EPOT];
+
+ fnorm = ems.fnorm;
+ fmax = ems.fmax;
+ nfmax = ems.a_fmax;
+
+ /* Set the initial step.
+ * since it will be multiplied by the non-normalized search direction
+ * vector (force vector the first time), we scale it by the
+ * norm of the force.
+ */
+
+ if (MASTER(cr)) {
+ fprintf(stderr,"Using %d BFGS correction steps.\n\n",nmaxcorr);
+ fprintf(stderr," F-max = %12.5e on atom %d\n",fmax,nfmax+1);
+ fprintf(stderr," F-Norm = %12.5e\n",fnorm/sqrt(state->natoms));
+ fprintf(stderr,"\n");
+ /* and copy to the log file too... */
+ fprintf(fplog,"Using %d BFGS correction steps.\n\n",nmaxcorr);
+ fprintf(fplog," F-max = %12.5e on atom %d\n",fmax,nfmax+1);
+ fprintf(fplog," F-Norm = %12.5e\n",fnorm/sqrt(state->natoms));
+ fprintf(fplog,"\n");
+ }
+
+ point=0;
+ for(i=0;i<n;i++)
+ if(!frozen[i])
+ dx[point][i] = ff[i]; /* Initial search direction */
+ else
+ dx[point][i] = 0;
+
+ stepsize = 1.0/fnorm;
+ converged = FALSE;
+
+ /* Start the loop over BFGS steps.
+ * Each successful step is counted, and we continue until
+ * we either converge or reach the max number of steps.
+ */
+
+ ncorr=0;
+
+ /* Set the gradient from the force */
+ converged = FALSE;
+ for(step=0; (number_steps<0 || (number_steps>=0 && step<=number_steps)) && !converged; step++) {
+
+ /* Write coordinates if necessary */
+ do_x = do_per_step(step,inputrec->nstxout);
+ do_f = do_per_step(step,inputrec->nstfout);
+
+ write_traj(fplog,cr,outf,MDOF_X | MDOF_F,
+ top_global,step,(real)step,state,state,f,f,NULL,NULL);
+
+ /* Do the linesearching in the direction dx[point][0..(n-1)] */
+
+ /* pointer to current direction - point=0 first time here */
+ s=dx[point];
+
+ /* calculate line gradient */
+ for(gpa=0,i=0;i<n;i++)
+ gpa-=s[i]*ff[i];
+
+ /* Calculate minimum allowed stepsize, before the average (norm)
+ * relative change in coordinate is smaller than precision
+ */
+ for(minstep=0,i=0;i<n;i++) {
+ tmp=fabs(xx[i]);
+ if(tmp<1.0)
+ tmp=1.0;
+ tmp = s[i]/tmp;
+ minstep += tmp*tmp;
+ }
+ minstep = GMX_REAL_EPS/sqrt(minstep/n);
+
+ if(stepsize<minstep) {
+ converged=TRUE;
+ break;
+ }
+
+ /* Store old forces and coordinates */
+ for(i=0;i<n;i++) {
+ lastx[i]=xx[i];
+ lastf[i]=ff[i];
+ }
+ Epot0=Epot;
+
+ first=TRUE;
+
+ for(i=0;i<n;i++)
+ xa[i]=xx[i];
+
+ /* Take a step downhill.
+ * In theory, we should minimize the function along this direction.
+ * That is quite possible, but it turns out to take 5-10 function evaluations
+ * for each line. However, we dont really need to find the exact minimum -
+ * it is much better to start a new BFGS step in a modified direction as soon
+ * as we are close to it. This will save a lot of energy evaluations.
+ *
+ * In practice, we just try to take a single step.
+ * If it worked (i.e. lowered the energy), we increase the stepsize but
+ * the continue straight to the next BFGS step without trying to find any minimum.
+ * If it didn't work (higher energy), there must be a minimum somewhere between
+ * the old position and the new one.
+ *
+ * Due to the finite numerical accuracy, it turns out that it is a good idea
+ * to even accept a SMALL increase in energy, if the derivative is still downhill.
+ * This leads to lower final energies in the tests I've done. / Erik
+ */
+ foundlower=FALSE;
+ EpotA = Epot0;
+ a = 0.0;
+ c = a + stepsize; /* reference position along line is zero */
+
+ /* Check stepsize first. We do not allow displacements
+ * larger than emstep.
+ */
+ do {
+ c = a + stepsize;
+ maxdelta=0;
+ for(i=0;i<n;i++) {
+ delta=c*s[i];
+ if(delta>maxdelta)
+ maxdelta=delta;
+ }
+ if(maxdelta>inputrec->em_stepsize)
+ stepsize*=0.1;
+ } while(maxdelta>inputrec->em_stepsize);
+
+ /* Take a trial step */
+ for (i=0; i<n; i++)
+ xc[i] = lastx[i] + c*s[i];
+
+ neval++;
+ /* Calculate energy for the trial step */
+ ems.s.x = (rvec *)xc;
+ ems.f = (rvec *)fc;
+ evaluate_energy(fplog,bVerbose,cr,
+ state,top_global,&ems,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,step,FALSE);
+ EpotC = ems.epot;
+
+ /* Calc derivative along line */
+ for(gpc=0,i=0; i<n; i++) {
+ gpc -= s[i]*fc[i]; /* f is negative gradient, thus the sign */
+ }
+ /* Sum the gradient along the line across CPUs */
+ if (PAR(cr))
+ gmx_sumd(1,&gpc,cr);
+
+ /* This is the max amount of increase in energy we tolerate */
+ tmp=sqrt(GMX_REAL_EPS)*fabs(EpotA);
+
+ /* Accept the step if the energy is lower, or if it is not significantly higher
+ * and the line derivative is still negative.
+ */
+ if(EpotC<EpotA || (gpc<0 && EpotC<(EpotA+tmp))) {
+ foundlower = TRUE;
+ /* Great, we found a better energy. Increase step for next iteration
+ * if we are still going down, decrease it otherwise
+ */
+ if(gpc<0)
+ stepsize *= 1.618034; /* The golden section */
+ else
+ stepsize *= 0.618034; /* 1/golden section */
+ } else {
+ /* New energy is the same or higher. We will have to do some work
+ * to find a smaller value in the interval. Take smaller step next time!
+ */
+ foundlower = FALSE;
+ stepsize *= 0.618034;
+ }
+
+ /* OK, if we didn't find a lower value we will have to locate one now - there must
+ * be one in the interval [a=0,c].
+ * The same thing is valid here, though: Don't spend dozens of iterations to find
+ * the line minimum. We try to interpolate based on the derivative at the endpoints,
+ * and only continue until we find a lower value. In most cases this means 1-2 iterations.
+ *
+ * I also have a safeguard for potentially really patological functions so we never
+ * take more than 20 steps before we give up ...
+ *
+ * If we already found a lower value we just skip this step and continue to the update.
+ */
+
+ if(!foundlower) {
+
+ nminstep=0;
+ do {
+ /* Select a new trial point.
+ * If the derivatives at points a & c have different sign we interpolate to zero,
+ * otherwise just do a bisection.
+ */
+
+ if(gpa<0 && gpc>0)
+ b = a + gpa*(a-c)/(gpc-gpa);
+ else
+ b = 0.5*(a+c);
+
+ /* safeguard if interpolation close to machine accuracy causes errors:
+ * never go outside the interval
+ */
+ if(b<=a || b>=c)
+ b = 0.5*(a+c);
+
+ /* Take a trial step */
+ for (i=0; i<n; i++)
+ xb[i] = lastx[i] + b*s[i];
+
+ neval++;
+ /* Calculate energy for the trial step */
+ ems.s.x = (rvec *)xb;
+ ems.f = (rvec *)fb;
+ evaluate_energy(fplog,bVerbose,cr,
+ state,top_global,&ems,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,step,FALSE);
+ EpotB = ems.epot;
+
+ fnorm = ems.fnorm;
+
+ for(gpb=0,i=0; i<n; i++)
+ gpb -= s[i]*fb[i]; /* f is negative gradient, thus the sign */
+
+ /* Sum the gradient along the line across CPUs */
+ if (PAR(cr))
+ gmx_sumd(1,&gpb,cr);
+
+ /* Keep one of the intervals based on the value of the derivative at the new point */
+ if(gpb>0) {
+ /* Replace c endpoint with b */
+ EpotC = EpotB;
+ c = b;
+ gpc = gpb;
+ /* swap coord pointers b/c */
+ xtmp = xb;
+ ftmp = fb;
+ xb = xc;
+ fb = fc;
+ xc = xtmp;
+ fc = ftmp;
+ } else {
+ /* Replace a endpoint with b */
+ EpotA = EpotB;
+ a = b;
+ gpa = gpb;
+ /* swap coord pointers a/b */
+ xtmp = xb;
+ ftmp = fb;
+ xb = xa;
+ fb = fa;
+ xa = xtmp;
+ fa = ftmp;
+ }
+
+ /*
+ * Stop search as soon as we find a value smaller than the endpoints,
+ * or if the tolerance is below machine precision.
+ * Never run more than 20 steps, no matter what.
+ */
+ nminstep++;
+ } while((EpotB>EpotA || EpotB>EpotC) && (nminstep<20));
+
+ if(fabs(EpotB-Epot0)<GMX_REAL_EPS || nminstep>=20) {
+ /* OK. We couldn't find a significantly lower energy.
+ * If ncorr==0 this was steepest descent, and then we give up.
+ * If not, reset memory to restart as steepest descent before quitting.
+ */
+ if(ncorr==0) {
+ /* Converged */
+ converged=TRUE;
+ break;
+ } else {
+ /* Reset memory */
+ ncorr=0;
+ /* Search in gradient direction */
+ for(i=0;i<n;i++)
+ dx[point][i]=ff[i];
+ /* Reset stepsize */
+ stepsize = 1.0/fnorm;
+ continue;
+ }
+ }
+
+ /* Select min energy state of A & C, put the best in xx/ff/Epot
+ */
+ if(EpotC<EpotA) {
+ Epot = EpotC;
+ /* Use state C */
+ for(i=0;i<n;i++) {
+ xx[i]=xc[i];
+ ff[i]=fc[i];
+ }
+ stepsize=c;
+ } else {
+ Epot = EpotA;
+ /* Use state A */
+ for(i=0;i<n;i++) {
+ xx[i]=xa[i];
+ ff[i]=fa[i];
+ }
+ stepsize=a;
+ }
+
+ } else {
+ /* found lower */
+ Epot = EpotC;
+ /* Use state C */
+ for(i=0;i<n;i++) {
+ xx[i]=xc[i];
+ ff[i]=fc[i];
+ }
+ stepsize=c;
+ }
+
+ /* Update the memory information, and calculate a new
+ * approximation of the inverse hessian
+ */
+
+ /* Have new data in Epot, xx, ff */
+ if(ncorr<nmaxcorr)
+ ncorr++;
+
+ for(i=0;i<n;i++) {
+ dg[point][i]=lastf[i]-ff[i];
+ dx[point][i]*=stepsize;
+ }
+
+ dgdg=0;
+ dgdx=0;
+ for(i=0;i<n;i++) {
+ dgdg+=dg[point][i]*dg[point][i];
+ dgdx+=dg[point][i]*dx[point][i];
+ }
+
+ diag=dgdx/dgdg;
+
+ rho[point]=1.0/dgdx;
+ point++;
+
+ if(point>=nmaxcorr)
+ point=0;
+
+ /* Update */
+ for(i=0;i<n;i++)
+ p[i]=ff[i];
+
+ cp=point;
+
+ /* Recursive update. First go back over the memory points */
+ for(k=0;k<ncorr;k++) {
+ cp--;
+ if(cp<0)
+ cp=ncorr-1;
+
+ sq=0;
+ for(i=0;i<n;i++)
+ sq+=dx[cp][i]*p[i];
+
+ alpha[cp]=rho[cp]*sq;
+
+ for(i=0;i<n;i++)
+ p[i] -= alpha[cp]*dg[cp][i];
+ }
+
+ for(i=0;i<n;i++)
+ p[i] *= diag;
+
+ /* And then go forward again */
+ for(k=0;k<ncorr;k++) {
+ yr = 0;
+ for(i=0;i<n;i++)
+ yr += p[i]*dg[cp][i];
+
+ beta = rho[cp]*yr;
+ beta = alpha[cp]-beta;
+
+ for(i=0;i<n;i++)
+ p[i] += beta*dx[cp][i];
+
+ cp++;
+ if(cp>=ncorr)
+ cp=0;
+ }
+
+ for(i=0;i<n;i++)
+ if(!frozen[i])
+ dx[point][i] = p[i];
+ else
+ dx[point][i] = 0;
+
+ stepsize=1.0;
+
+ /* Test whether the convergence criterion is met */
+ get_f_norm_max(cr,&(inputrec->opts),mdatoms,f,&fnorm,&fmax,&nfmax);
+
+ /* Print it if necessary */
+ if (MASTER(cr)) {
+ if(bVerbose)
+ fprintf(stderr,"\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
+ step,Epot,fnorm/sqrt(state->natoms),fmax,nfmax+1);
+ /* Store the new (lower) energies */
+ upd_mdebin(mdebin,FALSE,FALSE,(double)step,
+ mdatoms->tmass,enerd,state,state->box,
+ NULL,NULL,vir,pres,NULL,mu_tot,constr);
+ do_log = do_per_step(step,inputrec->nstlog);
+ do_ene = do_per_step(step,inputrec->nstenergy);
+ if(do_log)
+ print_ebin_header(fplog,step,step,state->lambda);
+ print_ebin(outf->fp_ene,do_ene,FALSE,FALSE,
+ do_log ? fplog : NULL,step,step,eprNORMAL,
+ TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+ }
+
+ /* Stop when the maximum force lies below tolerance.
+ * If we have reached machine precision, converged is already set to true.
+ */
+
+ converged = converged || (fmax < inputrec->em_tol);
+
+ } /* End of the loop */
+
+ if(converged)
+ step--; /* we never took that last step in this case */
+
+ if(fmax>inputrec->em_tol)
+ {
+ if (MASTER(cr))
+ {
+ warn_step(stderr,inputrec->em_tol,step-1==number_steps,FALSE);
+ warn_step(fplog ,inputrec->em_tol,step-1==number_steps,FALSE);
+ }
+ converged = FALSE;
+ }
+
+ /* If we printed energy and/or logfile last step (which was the last step)
+ * we don't have to do it again, but otherwise print the final values.
+ */
+ if(!do_log) /* Write final value to log since we didn't do anythin last step */
+ print_ebin_header(fplog,step,step,state->lambda);
+ if(!do_ene || !do_log) /* Write final energy file entries */
+ print_ebin(outf->fp_ene,!do_ene,FALSE,FALSE,
+ !do_log ? fplog : NULL,step,step,eprNORMAL,
+ TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+
+ /* Print some stuff... */
+ if (MASTER(cr))
+ fprintf(stderr,"\nwriting lowest energy coordinates.\n");
+
+ /* IMPORTANT!
+ * For accurate normal mode calculation it is imperative that we
+ * store the last conformation into the full precision binary trajectory.
+ *
+ * However, we should only do it if we did NOT already write this step
+ * above (which we did if do_x or do_f was true).
+ */
+ do_x = !do_per_step(step,inputrec->nstxout);
+ do_f = !do_per_step(step,inputrec->nstfout);
+ write_em_traj(fplog,cr,outf,do_x,do_f,ftp2fn(efSTO,nfile,fnm),
+ top_global,inputrec,step,
+ &ems,state,f);
+
+ if (MASTER(cr)) {
+ print_converged(stderr,LBFGS,inputrec->em_tol,step,converged,
+ number_steps,Epot,fmax,nfmax,fnorm/sqrt(state->natoms));
+ print_converged(fplog,LBFGS,inputrec->em_tol,step,converged,
+ number_steps,Epot,fmax,nfmax,fnorm/sqrt(state->natoms));
+
+ fprintf(fplog,"\nPerformed %d energy evaluations in total.\n",neval);
+ }
+
+ finish_em(fplog,cr,outf,runtime,wcycle);
+
+ /* To print the actual number of steps we needed somewhere */
+ runtime->nsteps_done = step;
+
+ return 0;
+} /* That's all folks */
+
+
+double do_steep(FILE *fplog,t_commrec *cr,
+ int nfile, const t_filenm fnm[],
+ const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
+ int nstglobalcomm,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int stepout,
+ t_inputrec *inputrec,
+ gmx_mtop_t *top_global,t_fcdata *fcd,
+ t_state *state_global,
+ t_mdatoms *mdatoms,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_edsam_t ed,
+ t_forcerec *fr,
+ int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ const char *SD="Steepest Descents";
+ em_state_t *s_min,*s_try;
+ rvec *f_global;
+ gmx_localtop_t *top;
+ gmx_enerdata_t *enerd;
+ rvec *f;
+ gmx_global_stat_t gstat;
+ t_graph *graph;
+ real stepsize,constepsize;
+ real ustep,dvdlambda,fnormn;
+ gmx_mdoutf_t *outf;
+ t_mdebin *mdebin;
+ gmx_bool bDone,bAbort,do_x,do_f;
+ tensor vir,pres;
+ rvec mu_tot;
+ int nsteps;
+ int count=0;
+ int steps_accepted=0;
+ /* not used */
+ real terminate=0;
+
+ s_min = init_em_state();
+ s_try = init_em_state();
+
+ /* Init em and store the local state in s_try */
+ init_em(fplog,SD,cr,inputrec,
+ state_global,top_global,s_try,&top,&f,&f_global,
+ nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
+ nfile,fnm,&outf,&mdebin);
+
+ /* Print to log file */
+ print_em_start(fplog,cr,runtime,wcycle,SD);
+
+ /* Set variables for stepsize (in nm). This is the largest
+ * step that we are going to make in any direction.
+ */
+ ustep = inputrec->em_stepsize;
+ stepsize = 0;
+
+ /* Max number of steps */
+ nsteps = inputrec->nsteps;
+
+ if (MASTER(cr))
+ /* Print to the screen */
+ sp_header(stderr,SD,inputrec->em_tol,nsteps);
+ if (fplog)
+ sp_header(fplog,SD,inputrec->em_tol,nsteps);
+
+ /**** HERE STARTS THE LOOP ****
+ * count is the counter for the number of steps
+ * bDone will be TRUE when the minimization has converged
+ * bAbort will be TRUE when nsteps steps have been performed or when
+ * the stepsize becomes smaller than is reasonable for machine precision
+ */
+ count = 0;
+ bDone = FALSE;
+ bAbort = FALSE;
+ while( !bDone && !bAbort ) {
+ bAbort = (nsteps >= 0) && (count == nsteps);
+
+ /* set new coordinates, except for first step */
+ if (count > 0) {
+ do_em_step(cr,inputrec,mdatoms,s_min,stepsize,s_min->f,s_try,
+ constr,top,nrnb,wcycle,count);
+ }
+
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,s_try,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,count,count==0);
+
+ if (MASTER(cr))
+ print_ebin_header(fplog,count,count,s_try->s.lambda);
+
+ if (count == 0)
+ s_min->epot = s_try->epot + 1;
+
+ /* Print it if necessary */
+ if (MASTER(cr)) {
+ if (bVerbose) {
+ fprintf(stderr,"Step=%5d, Dmax= %6.1e nm, Epot= %12.5e Fmax= %11.5e, atom= %d%c",
+ count,ustep,s_try->epot,s_try->fmax,s_try->a_fmax+1,
+ (s_try->epot < s_min->epot) ? '\n' : '\r');
+ }
+
+ if (s_try->epot < s_min->epot) {
+ /* Store the new (lower) energies */
+ upd_mdebin(mdebin,FALSE,FALSE,(double)count,
+ mdatoms->tmass,enerd,&s_try->s,s_try->s.box,
+ NULL,NULL,vir,pres,NULL,mu_tot,constr);
+ print_ebin(outf->fp_ene,TRUE,
+ do_per_step(steps_accepted,inputrec->nstdisreout),
+ do_per_step(steps_accepted,inputrec->nstorireout),
+ fplog,count,count,eprNORMAL,TRUE,
+ mdebin,fcd,&(top_global->groups),&(inputrec->opts));
+ fflush(fplog);
+ }
+ }
+
+ /* Now if the new energy is smaller than the previous...
+ * or if this is the first step!
+ * or if we did random steps!
+ */
+
+ if ( (count==0) || (s_try->epot < s_min->epot) ) {
+ steps_accepted++;
+
+ /* Test whether the convergence criterion is met... */
+ bDone = (s_try->fmax < inputrec->em_tol);
+
+ /* Copy the arrays for force, positions and energy */
+ /* The 'Min' array always holds the coords and forces of the minimal
+ sampled energy */
+ swap_em_state(s_min,s_try);
+ if (count > 0)
+ ustep *= 1.2;
+
+ /* Write to trn, if necessary */
+ do_x = do_per_step(steps_accepted,inputrec->nstxout);
+ do_f = do_per_step(steps_accepted,inputrec->nstfout);
+ write_em_traj(fplog,cr,outf,do_x,do_f,NULL,
+ top_global,inputrec,count,
+ s_min,state_global,f_global);
+ }
+ else {
+ /* If energy is not smaller make the step smaller... */
+ ustep *= 0.5;
+
+ if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count) {
+ /* Reload the old state */
+ em_dd_partition_system(fplog,count,cr,top_global,inputrec,
+ s_min,top,mdatoms,fr,vsite,constr,
+ nrnb,wcycle);
+ }
+ }
+
+ /* Determine new step */
+ stepsize = ustep/s_min->fmax;
+
+ /* Check if stepsize is too small, with 1 nm as a characteristic length */
+#ifdef GMX_DOUBLE
+ if (count == nsteps || ustep < 1e-12)
+#else
+ if (count == nsteps || ustep < 1e-6)
+#endif
+ {
+ if (MASTER(cr))
+ {
+ warn_step(stderr,inputrec->em_tol,count==nsteps,constr!=NULL);
+ warn_step(fplog ,inputrec->em_tol,count==nsteps,constr!=NULL);
+ }
+ bAbort=TRUE;
+ }
+
+ count++;
+ } /* End of the loop */
+
+ /* Print some shit... */
+ if (MASTER(cr))
+ fprintf(stderr,"\nwriting lowest energy coordinates.\n");
+ write_em_traj(fplog,cr,outf,TRUE,inputrec->nstfout,ftp2fn(efSTO,nfile,fnm),
+ top_global,inputrec,count,
+ s_min,state_global,f_global);
+
+ fnormn = s_min->fnorm/sqrt(state_global->natoms);
+
+ if (MASTER(cr)) {
+ print_converged(stderr,SD,inputrec->em_tol,count,bDone,nsteps,
+ s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
+ print_converged(fplog,SD,inputrec->em_tol,count,bDone,nsteps,
+ s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
+ }
+
+ finish_em(fplog,cr,outf,runtime,wcycle);
+
+ /* To print the actual number of steps we needed somewhere */
+ inputrec->nsteps=count;
+
+ runtime->nsteps_done = count;
+
+ return 0;
+} /* That's all folks */
+
+
+double do_nm(FILE *fplog,t_commrec *cr,
+ int nfile,const t_filenm fnm[],
+ const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
+ int nstglobalcomm,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int stepout,
+ t_inputrec *inputrec,
+ gmx_mtop_t *top_global,t_fcdata *fcd,
+ t_state *state_global,
+ t_mdatoms *mdatoms,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_edsam_t ed,
+ t_forcerec *fr,
+ int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ const char *NM = "Normal Mode Analysis";
+ gmx_mdoutf_t *outf;
+ int natoms,atom,d;
+ int nnodes,node;
+ rvec *f_global;
+ gmx_localtop_t *top;
+ gmx_enerdata_t *enerd;
+ rvec *f;
+ gmx_global_stat_t gstat;
+ t_graph *graph;
+ real t,lambda;
+ gmx_bool bNS;
+ tensor vir,pres;
+ rvec mu_tot;
+ rvec *fneg,*dfdx;
+ gmx_bool bSparse; /* use sparse matrix storage format */
+ size_t sz;
+ gmx_sparsematrix_t * sparse_matrix = NULL;
+ real * full_matrix = NULL;
+ em_state_t * state_work;
+
+ /* added with respect to mdrun */
+ int i,j,k,row,col;
+ real der_range=10.0*sqrt(GMX_REAL_EPS);
+ real x_min;
+ real fnorm,fmax;
+
+ if (constr != NULL)
+ {
+ gmx_fatal(FARGS,"Constraints present with Normal Mode Analysis, this combination is not supported");
+ }
+
+ state_work = init_em_state();
+
+ /* Init em and store the local state in state_minimum */
+ init_em(fplog,NM,cr,inputrec,
+ state_global,top_global,state_work,&top,
+ &f,&f_global,
+ nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
+ nfile,fnm,&outf,NULL);
+
+ natoms = top_global->natoms;
+ snew(fneg,natoms);
+ snew(dfdx,natoms);
+
+#ifndef GMX_DOUBLE
+ if (MASTER(cr))
+ {
+ fprintf(stderr,
+ "NOTE: This version of Gromacs has been compiled in single precision,\n"
+ " which MIGHT not be accurate enough for normal mode analysis.\n"
+ " Gromacs now uses sparse matrix storage, so the memory requirements\n"
+ " are fairly modest even if you recompile in double precision.\n\n");
+ }
+#endif
+
+ /* Check if we can/should use sparse storage format.
+ *
+ * Sparse format is only useful when the Hessian itself is sparse, which it
+ * will be when we use a cutoff.
+ * For small systems (n<1000) it is easier to always use full matrix format, though.
+ */
+ if(EEL_FULL(fr->eeltype) || fr->rlist==0.0)
+ {
+ fprintf(stderr,"Non-cutoff electrostatics used, forcing full Hessian format.\n");
+ bSparse = FALSE;
+ }
+ else if(top_global->natoms < 1000)
+ {
+ fprintf(stderr,"Small system size (N=%d), using full Hessian format.\n",top_global->natoms);
+ bSparse = FALSE;
+ }
+ else
+ {
+ fprintf(stderr,"Using compressed symmetric sparse Hessian format.\n");
+ bSparse = TRUE;
+ }
+
+ sz = DIM*top_global->natoms;
+
+ fprintf(stderr,"Allocating Hessian memory...\n\n");
+
+ if(bSparse)
+ {
+ sparse_matrix=gmx_sparsematrix_init(sz);
+ sparse_matrix->compressed_symmetric = TRUE;
+ }
+ else
+ {
+ snew(full_matrix,sz*sz);
+ }
+
+ /* Initial values */
+ t = inputrec->init_t;
+ lambda = inputrec->init_lambda;
+
+ init_nrnb(nrnb);
+
+ where();
+
+ /* Write start time and temperature */
+ print_em_start(fplog,cr,runtime,wcycle,NM);
+
+ /* fudge nr of steps to nr of atoms */
+ inputrec->nsteps = natoms*2;
+
+ if (MASTER(cr))
+ {
+ fprintf(stderr,"starting normal mode calculation '%s'\n%d steps.\n\n",
+ *(top_global->name),(int)inputrec->nsteps);
+ }
+
+ nnodes = cr->nnodes;
+
+ /* Make evaluate_energy do a single node force calculation */
+ cr->nnodes = 1;
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,state_work,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,-1,TRUE);
+ cr->nnodes = nnodes;
+
+ /* if forces are not small, warn user */
+ get_state_f_norm_max(cr,&(inputrec->opts),mdatoms,state_work);
+
+ if (MASTER(cr))
+ {
+ fprintf(stderr,"Maximum force:%12.5e\n",state_work->fmax);
+ if (state_work->fmax > 1.0e-3)
+ {
+ fprintf(stderr,"Maximum force probably not small enough to");
+ fprintf(stderr," ensure that you are in an \nenergy well. ");
+ fprintf(stderr,"Be aware that negative eigenvalues may occur");
+ fprintf(stderr," when the\nresulting matrix is diagonalized.\n");
+ }
+ }
+
+ /***********************************************************
+ *
+ * Loop over all pairs in matrix
+ *
+ * do_force called twice. Once with positive and
+ * once with negative displacement
+ *
+ ************************************************************/
+
+ /* Steps are divided one by one over the nodes */
+ for(atom=cr->nodeid; atom<natoms; atom+=nnodes)
+ {
+
+ for (d=0; d<DIM; d++)
+ {
+ x_min = state_work->s.x[atom][d];
+
+ state_work->s.x[atom][d] = x_min - der_range;
+
+ /* Make evaluate_energy do a single node force calculation */
+ cr->nnodes = 1;
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,state_work,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,atom*2,FALSE);
+
+ for(i=0; i<natoms; i++)
+ {
+ copy_rvec(state_work->f[i], fneg[i]);
+ }
+
+ state_work->s.x[atom][d] = x_min + der_range;
+
+ evaluate_energy(fplog,bVerbose,cr,
+ state_global,top_global,state_work,top,
+ inputrec,nrnb,wcycle,gstat,
+ vsite,constr,fcd,graph,mdatoms,fr,
+ mu_tot,enerd,vir,pres,atom*2+1,FALSE);
+ cr->nnodes = nnodes;
+
+ /* x is restored to original */
+ state_work->s.x[atom][d] = x_min;
+
+ for(j=0; j<natoms; j++)
+ {
+ for (k=0; (k<DIM); k++)
+ {
+ dfdx[j][k] =
+ -(state_work->f[j][k] - fneg[j][k])/(2*der_range);
+ }
+ }
+
+ if (!MASTER(cr))
+ {
+#ifdef GMX_MPI
+#ifdef GMX_DOUBLE
+#define mpi_type MPI_DOUBLE
+#else
+#define mpi_type MPI_FLOAT
+#endif
+ MPI_Send(dfdx[0],natoms*DIM,mpi_type,MASTERNODE(cr),cr->nodeid,
+ cr->mpi_comm_mygroup);
+#endif
+ }
+ else
+ {
+ for(node=0; (node<nnodes && atom+node<natoms); node++)
+ {
+ if (node > 0)
+ {
+#ifdef GMX_MPI
+ MPI_Status stat;
+ MPI_Recv(dfdx[0],natoms*DIM,mpi_type,node,node,
+ cr->mpi_comm_mygroup,&stat);
+#undef mpi_type
+#endif
+ }
+
+ row = (atom + node)*DIM + d;
+
+ for(j=0; j<natoms; j++)
+ {
+ for(k=0; k<DIM; k++)
+ {
+ col = j*DIM + k;
+
+ if (bSparse)
+ {
+ if (col >= row && dfdx[j][k] != 0.0)
+ {
+ gmx_sparsematrix_increment_value(sparse_matrix,
+ row,col,dfdx[j][k]);
+ }
+ }
+ else
+ {
+ full_matrix[row*sz+col] = dfdx[j][k];
+ }
+ }
+ }
+ }
+ }
+
+ if (bVerbose && fplog)
+ {
+ fflush(fplog);
+ }
+ }
+ /* write progress */
+ if (MASTER(cr) && bVerbose)
+ {
+ fprintf(stderr,"\rFinished step %d out of %d",
+ min(atom+nnodes,natoms),natoms);
+ fflush(stderr);
+ }
+ }
+
+ if (MASTER(cr))
+ {
+ fprintf(stderr,"\n\nWriting Hessian...\n");
+ gmx_mtxio_write(ftp2fn(efMTX,nfile,fnm),sz,sz,full_matrix,sparse_matrix);
+ }
+
+ finish_em(fplog,cr,outf,runtime,wcycle);
+
+ runtime->nsteps_done = natoms*2;
+
+ return 0;
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef GMX_THREAD_SHM_FDECOMP
+#include <pthread.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include "sysstuff.h"
+#include "smalloc.h"
+#include "macros.h"
+#include "maths.h"
+#include "vec.h"
+#include "network.h"
+#include "nsgrid.h"
+#include "force.h"
+#include "nonbonded.h"
+#include "ns.h"
+#include "pbc.h"
+#include "names.h"
+#include "gmx_fatal.h"
+#include "nrnb.h"
+#include "txtdump.h"
+#include "mtop_util.h"
+
+#include "domdec.h"
+
+
+/*
+ * E X C L U S I O N H A N D L I N G
+ */
+
+#ifdef DEBUG
+static void SETEXCL_(t_excl e[],atom_id i,atom_id j)
+{ e[j] = e[j] | (1<<i); }
+static void RMEXCL_(t_excl e[],atom_id i,atom_id j)
+{ e[j]=e[j] & ~(1<<i); }
+static gmx_bool ISEXCL_(t_excl e[],atom_id i,atom_id j)
+{ return (gmx_bool)(e[j] & (1<<i)); }
+static gmx_bool NOTEXCL_(t_excl e[],atom_id i,atom_id j)
+{ return !(ISEXCL(e,i,j)); }
+#else
+#define SETEXCL(e,i,j) (e)[((atom_id) (j))] |= (1<<((atom_id) (i)))
+#define RMEXCL(e,i,j) (e)[((atom_id) (j))] &= (~(1<<((atom_id) (i))))
+#define ISEXCL(e,i,j) (gmx_bool) ((e)[((atom_id) (j))] & (1<<((atom_id) (i))))
+#define NOTEXCL(e,i,j) !(ISEXCL(e,i,j))
+#endif
+
+/************************************************
+ *
+ * U T I L I T I E S F O R N S
+ *
+ ************************************************/
+
+static void reallocate_nblist(t_nblist *nl)
+{
+ if (gmx_debug_at)
+ {
+ fprintf(debug,"reallocating neigborlist il_code=%d, maxnri=%d\n",
+ nl->il_code,nl->maxnri);
+ }
+ srenew(nl->iinr, nl->maxnri);
+ if (nl->enlist == enlistCG_CG)
+ {
+ srenew(nl->iinr_end,nl->maxnri);
+ }
+ srenew(nl->gid, nl->maxnri);
+ srenew(nl->shift, nl->maxnri);
+ srenew(nl->jindex, nl->maxnri+1);
+}
+
+/* ivdw/icoul are used to determine the type of interaction, so we
+ * can set an innerloop index here. The obvious choice for this would have
+ * been the vdwtype/coultype values in the forcerecord, but unfortunately
+ * those types are braindead - for instance both Buckingham and normal
+ * Lennard-Jones use the same value (evdwCUT), and a separate gmx_boolean variable
+ * to determine which interaction is used. There is further no special value
+ * for 'no interaction'. For backward compatibility with old TPR files we won't
+ * change this in the 3.x series, so when calling this routine you should use:
+ *
+ * icoul=0 no coulomb interaction
+ * icoul=1 cutoff standard coulomb
+ * icoul=2 reaction-field coulomb
+ * icoul=3 tabulated coulomb
+ *
+ * ivdw=0 no vdw interaction
+ * ivdw=1 standard L-J interaction
+ * ivdw=2 Buckingham
+ * ivdw=3 tabulated vdw.
+ *
+ * Kind of ugly, but it works.
+ */
+static void init_nblist(t_nblist *nl_sr,t_nblist *nl_lr,
+ int maxsr,int maxlr,
+ int ivdw, int icoul,
+ gmx_bool bfree, int enlist)
+{
+ t_nblist *nl;
+ int homenr;
+ int i,nn;
+
+ int inloop[20] =
+ {
+ eNR_NBKERNEL_NONE,
+ eNR_NBKERNEL010,
+ eNR_NBKERNEL020,
+ eNR_NBKERNEL030,
+ eNR_NBKERNEL100,
+ eNR_NBKERNEL110,
+ eNR_NBKERNEL120,
+ eNR_NBKERNEL130,
+ eNR_NBKERNEL200,
+ eNR_NBKERNEL210,
+ eNR_NBKERNEL220,
+ eNR_NBKERNEL230,
+ eNR_NBKERNEL300,
+ eNR_NBKERNEL310,
+ eNR_NBKERNEL320,
+ eNR_NBKERNEL330,
+ eNR_NBKERNEL400,
+ eNR_NBKERNEL410,
+ eNR_NBKERNEL_NONE,
+ eNR_NBKERNEL430
+ };
+
+ for(i=0; (i<2); i++)
+ {
+ nl = (i == 0) ? nl_sr : nl_lr;
+ homenr = (i == 0) ? maxsr : maxlr;
+
+ if (nl == NULL)
+ {
+ continue;
+ }
+
+ /* Set coul/vdw in neighborlist, and for the normal loops we determine
+ * an index of which one to call.
+ */
+ nl->ivdw = ivdw;
+ nl->icoul = icoul;
+ nl->free_energy = bfree;
+
+ if (bfree)
+ {
+ nl->enlist = enlistATOM_ATOM;
+ nl->il_code = eNR_NBKERNEL_FREE_ENERGY;
+ }
+ else
+ {
+ nl->enlist = enlist;
+
+ nn = inloop[4*icoul + ivdw];
+
+ /* solvent loops follow directly after the corresponding
+ * ordinary loops, in the order:
+ *
+ * SPC, SPC-SPC, TIP4p, TIP4p-TIP4p
+ *
+ */
+ switch (enlist) {
+ case enlistATOM_ATOM:
+ case enlistCG_CG:
+ break;
+ case enlistSPC_ATOM: nn += 1; break;
+ case enlistSPC_SPC: nn += 2; break;
+ case enlistTIP4P_ATOM: nn += 3; break;
+ case enlistTIP4P_TIP4P: nn += 4; break;
+ }
+
+ nl->il_code = nn;
+ }
+
+ if (debug)
+ fprintf(debug,"Initiating neighbourlist type %d for %s interactions,\nwith %d SR, %d LR atoms.\n",
+ nl->il_code,ENLISTTYPE(enlist),maxsr,maxlr);
+
+ /* maxnri is influenced by the number of shifts (maximum is 8)
+ * and the number of energy groups.
+ * If it is not enough, nl memory will be reallocated during the run.
+ * 4 seems to be a reasonable factor, which only causes reallocation
+ * during runs with tiny and many energygroups.
+ */
+ nl->maxnri = homenr*4;
+ nl->maxnrj = 0;
+ nl->maxlen = 0;
+ nl->nri = -1;
+ nl->nrj = 0;
+ nl->iinr = NULL;
+ nl->gid = NULL;
+ nl->shift = NULL;
+ nl->jindex = NULL;
+ reallocate_nblist(nl);
+ nl->jindex[0] = 0;
+#ifdef GMX_THREAD_SHM_FDECOMP
+ nl->counter = 0;
+ snew(nl->mtx,1);
+ pthread_mutex_init(nl->mtx,NULL);
+#endif
+ }
+}
+
+void init_neighbor_list(FILE *log,t_forcerec *fr,int homenr)
+{
+ /* Make maxlr tunable! (does not seem to be a big difference though)
+ * This parameter determines the number of i particles in a long range
+ * neighbourlist. Too few means many function calls, too many means
+ * cache trashing.
+ */
+ int maxsr,maxsr_wat,maxlr,maxlr_wat;
+ int icoul,icoulf,ivdw;
+ int solvent;
+ int enlist_def,enlist_w,enlist_ww;
+ int i;
+ t_nblists *nbl;
+
+ /* maxsr = homenr-fr->nWatMol*3; */
+ maxsr = homenr;
+
+ if (maxsr < 0)
+ {
+ gmx_fatal(FARGS,"%s, %d: Negative number of short range atoms.\n"
+ "Call your Gromacs dealer for assistance.",__FILE__,__LINE__);
+ }
+ /* This is just for initial allocation, so we do not reallocate
+ * all the nlist arrays many times in a row.
+ * The numbers seem very accurate, but they are uncritical.
+ */
+ maxsr_wat = min(fr->nWatMol,(homenr+2)/3);
+ if (fr->bTwinRange)
+ {
+ maxlr = 50;
+ maxlr_wat = min(maxsr_wat,maxlr);
+ }
+ else
+ {
+ maxlr = maxlr_wat = 0;
+ }
+
+ /* Determine the values for icoul/ivdw. */
+ /* Start with GB */
+ if(fr->bGB)
+ {
+ icoul=4;
+ }
+ else if (fr->bcoultab)
+ {
+ icoul = 3;
+ }
+ else if (EEL_RF(fr->eeltype))
+ {
+ icoul = 2;
+ }
+ else
+ {
+ icoul = 1;
+ }
+
+ if (fr->bvdwtab)
+ {
+ ivdw = 3;
+ }
+ else if (fr->bBHAM)
+ {
+ ivdw = 2;
+ }
+ else
+ {
+ ivdw = 1;
+ }
+
+ fr->ns.bCGlist = (getenv("GMX_NBLISTCG") != 0);
+ if (!fr->ns.bCGlist)
+ {
+ enlist_def = enlistATOM_ATOM;
+ }
+ else
+ {
+ enlist_def = enlistCG_CG;
+ if (log != NULL)
+ {
+ fprintf(log,"\nUsing charge-group - charge-group neighbor lists and kernels\n\n");
+ }
+ if (!fr->bExcl_IntraCGAll_InterCGNone)
+ {
+ gmx_fatal(FARGS,"The charge-group - charge-group force loops only support systems with all intra-cg interactions excluded and no inter-cg exclusions, this is not the case for this system.");
+ }
+ }
+
+ if (fr->solvent_opt == esolTIP4P) {
+ enlist_w = enlistTIP4P_ATOM;
+ enlist_ww = enlistTIP4P_TIP4P;
+ } else {
+ enlist_w = enlistSPC_ATOM;
+ enlist_ww = enlistSPC_SPC;
+ }
+
+ for(i=0; i<fr->nnblists; i++)
+ {
+ nbl = &(fr->nblists[i]);
+ init_nblist(&nbl->nlist_sr[eNL_VDWQQ],&nbl->nlist_lr[eNL_VDWQQ],
+ maxsr,maxlr,ivdw,icoul,FALSE,enlist_def);
+ init_nblist(&nbl->nlist_sr[eNL_VDW],&nbl->nlist_lr[eNL_VDW],
+ maxsr,maxlr,ivdw,0,FALSE,enlist_def);
+ init_nblist(&nbl->nlist_sr[eNL_QQ],&nbl->nlist_lr[eNL_QQ],
+ maxsr,maxlr,0,icoul,FALSE,enlist_def);
+ init_nblist(&nbl->nlist_sr[eNL_VDWQQ_WATER],&nbl->nlist_lr[eNL_VDWQQ_WATER],
+ maxsr_wat,maxlr_wat,ivdw,icoul, FALSE,enlist_w);
+ init_nblist(&nbl->nlist_sr[eNL_QQ_WATER],&nbl->nlist_lr[eNL_QQ_WATER],
+ maxsr_wat,maxlr_wat,0,icoul, FALSE,enlist_w);
+ init_nblist(&nbl->nlist_sr[eNL_VDWQQ_WATERWATER],&nbl->nlist_lr[eNL_VDWQQ_WATERWATER],
+ maxsr_wat,maxlr_wat,ivdw,icoul, FALSE,enlist_ww);
+ init_nblist(&nbl->nlist_sr[eNL_QQ_WATERWATER],&nbl->nlist_lr[eNL_QQ_WATERWATER],
+ maxsr_wat,maxlr_wat,0,icoul, FALSE,enlist_ww);
+
+ if (fr->efep != efepNO)
+ {
+ if (fr->bEwald)
+ {
+ icoulf = 5;
+ }
+ else
+ {
+ icoulf = icoul;
+ }
+
+ init_nblist(&nbl->nlist_sr[eNL_VDWQQ_FREE],&nbl->nlist_lr[eNL_VDWQQ_FREE],
+ maxsr,maxlr,ivdw,icoulf,TRUE,enlistATOM_ATOM);
+ init_nblist(&nbl->nlist_sr[eNL_VDW_FREE],&nbl->nlist_lr[eNL_VDW_FREE],
+ maxsr,maxlr,ivdw,0,TRUE,enlistATOM_ATOM);
+ init_nblist(&nbl->nlist_sr[eNL_QQ_FREE],&nbl->nlist_lr[eNL_QQ_FREE],
+ maxsr,maxlr,0,icoulf,TRUE,enlistATOM_ATOM);
+ }
+ }
+ /* QMMM MM list */
+ if (fr->bQMMM && fr->qr->QMMMscheme != eQMMMschemeoniom)
+ {
+ init_nblist(&fr->QMMMlist,NULL,
+ maxsr,maxlr,0,icoul,FALSE,enlistATOM_ATOM);
+ }
+
+ fr->ns.nblist_initialized=TRUE;
+}
+
+static void reset_nblist(t_nblist *nl)
+{
+ nl->nri = -1;
+ nl->nrj = 0;
+ nl->maxlen = 0;
+ if (nl->jindex)
+ {
+ nl->jindex[0] = 0;
+ }
+}
+
+static void reset_neighbor_list(t_forcerec *fr,gmx_bool bLR,int nls,int eNL)
+{
+ int n,i;
+
+ if (bLR)
+ {
+ reset_nblist(&(fr->nblists[nls].nlist_lr[eNL]));
+ }
+ else
+ {
+ for(n=0; n<fr->nnblists; n++)
+ {
+ for(i=0; i<eNL_NR; i++)
+ {
+ reset_nblist(&(fr->nblists[n].nlist_sr[i]));
+ }
+ }
+ if (fr->bQMMM)
+ {
+ /* only reset the short-range nblist */
+ reset_nblist(&(fr->QMMMlist));
+ }
+ }
+}
+
+
+
+
+static inline void new_i_nblist(t_nblist *nlist,
+ gmx_bool bLR,atom_id i_atom,int shift,int gid)
+{
+ int i,k,nri,nshift;
+
+ nri = nlist->nri;
+
+ /* Check whether we have to increase the i counter */
+ if ((nri == -1) ||
+ (nlist->iinr[nri] != i_atom) ||
+ (nlist->shift[nri] != shift) ||
+ (nlist->gid[nri] != gid))
+ {
+ /* This is something else. Now see if any entries have
+ * been added in the list of the previous atom.
+ */
+ if ((nri == -1) ||
+ ((nlist->jindex[nri+1] > nlist->jindex[nri]) &&
+ (nlist->gid[nri] != -1)))
+ {
+ /* If so increase the counter */
+ nlist->nri++;
+ nri++;
+ if (nlist->nri >= nlist->maxnri)
+ {
+ nlist->maxnri += over_alloc_large(nlist->nri);
+ reallocate_nblist(nlist);
+ }
+ }
+ /* Set the number of neighbours and the atom number */
+ nlist->jindex[nri+1] = nlist->jindex[nri];
+ nlist->iinr[nri] = i_atom;
+ nlist->gid[nri] = gid;
+ nlist->shift[nri] = shift;
+ }
+}
+
+static inline void close_i_nblist(t_nblist *nlist)
+{
+ int nri = nlist->nri;
+ int len;
+
+ if (nri >= 0)
+ {
+ nlist->jindex[nri+1] = nlist->nrj;
+
+ len=nlist->nrj - nlist->jindex[nri];
+
+ /* nlist length for water i molecules is treated statically
+ * in the innerloops
+ */
+ if (len > nlist->maxlen)
+ {
+ nlist->maxlen = len;
+ }
+ }
+}
+
+static inline void close_nblist(t_nblist *nlist)
+{
+ /* Only close this nblist when it has been initialized.
+ * Avoid the creation of i-lists with no j-particles.
+ */
+ if (nlist->nrj == 0)
+ {
+ /* Some assembly kernels do not support empty lists,
+ * make sure here that we don't generate any empty lists.
+ * With the current ns code this branch is taken in two cases:
+ * No i-particles at all: nri=-1 here
+ * There are i-particles, but no j-particles; nri=0 here
+ */
+ nlist->nri = 0;
+ }
+ else
+ {
+ /* Close list number nri by incrementing the count */
+ nlist->nri++;
+ }
+}
+
+static inline void close_neighbor_list(t_forcerec *fr,gmx_bool bLR,int nls,int eNL,
+ gmx_bool bMakeQMMMnblist)
+{
+ int n,i;
+
+ if (bMakeQMMMnblist) {
+ if (!bLR)
+ {
+ close_nblist(&(fr->QMMMlist));
+ }
+ }
+ else
+ {
+ if (bLR)
+ {
+ close_nblist(&(fr->nblists[nls].nlist_lr[eNL]));
+ }
+ else
+ {
+ for(n=0; n<fr->nnblists; n++)
+ {
+ for(i=0; (i<eNL_NR); i++)
+ {
+ close_nblist(&(fr->nblists[n].nlist_sr[i]));
+ }
+ }
+ }
+ }
+}
+
+static inline void add_j_to_nblist(t_nblist *nlist,atom_id j_atom,gmx_bool bLR)
+{
+ int nrj=nlist->nrj;
+
+ if (nlist->nrj >= nlist->maxnrj)
+ {
+ nlist->maxnrj = over_alloc_small(nlist->nrj + 1);
+ if (gmx_debug_at)
+ fprintf(debug,"Increasing %s nblist %s j size to %d\n",
+ bLR ? "LR" : "SR",nrnb_str(nlist->il_code),nlist->maxnrj);
+
+ srenew(nlist->jjnr,nlist->maxnrj);
+ }
+
+ nlist->jjnr[nrj] = j_atom;
+ nlist->nrj ++;
+}
+
+static inline void add_j_to_nblist_cg(t_nblist *nlist,
+ atom_id j_start,int j_end,
+ t_excl *bexcl,gmx_bool bLR)
+{
+ int nrj=nlist->nrj;
+ int j;
+
+ if (nlist->nrj >= nlist->maxnrj)
+ {
+ nlist->maxnrj = over_alloc_small(nlist->nrj + 1);
+ if (gmx_debug_at)
+ fprintf(debug,"Increasing %s nblist %s j size to %d\n",
+ bLR ? "LR" : "SR",nrnb_str(nlist->il_code),nlist->maxnrj);
+
+ srenew(nlist->jjnr ,nlist->maxnrj);
+ srenew(nlist->jjnr_end,nlist->maxnrj);
+ srenew(nlist->excl ,nlist->maxnrj*MAX_CGCGSIZE);
+ }
+
+ nlist->jjnr[nrj] = j_start;
+ nlist->jjnr_end[nrj] = j_end;
+
+ if (j_end - j_start > MAX_CGCGSIZE)
+ {
+ gmx_fatal(FARGS,"The charge-group - charge-group neighborlist do not support charge groups larger than %d, found a charge group of size %d",MAX_CGCGSIZE,j_end-j_start);
+ }
+
+ /* Set the exclusions */
+ for(j=j_start; j<j_end; j++)
+ {
+ nlist->excl[nrj*MAX_CGCGSIZE + j - j_start] = bexcl[j];
+ }
+
+ nlist->nrj ++;
+}
+
+typedef void
+put_in_list_t(gmx_bool bHaveVdW[],
+ int ngid,
+ t_mdatoms * md,
+ int icg,
+ int jgid,
+ int nj,
+ atom_id jjcg[],
+ atom_id index[],
+ t_excl bExcl[],
+ int shift,
+ t_forcerec * fr,
+ gmx_bool bLR,
+ gmx_bool bDoVdW,
+ gmx_bool bDoCoul);
+
+static void
+put_in_list_at(gmx_bool bHaveVdW[],
+ int ngid,
+ t_mdatoms * md,
+ int icg,
+ int jgid,
+ int nj,
+ atom_id jjcg[],
+ atom_id index[],
+ t_excl bExcl[],
+ int shift,
+ t_forcerec * fr,
+ gmx_bool bLR,
+ gmx_bool bDoVdW,
+ gmx_bool bDoCoul)
+{
+ /* The a[] index has been removed,
+ * to put it back in i_atom should be a[i0] and jj should be a[jj].
+ */
+ t_nblist * vdwc;
+ t_nblist * vdw;
+ t_nblist * coul;
+ t_nblist * vdwc_free = NULL;
+ t_nblist * vdw_free = NULL;
+ t_nblist * coul_free = NULL;
+ t_nblist * vdwc_ww = NULL;
+ t_nblist * coul_ww = NULL;
+
+ int i,j,jcg,igid,gid,nbl_ind,ind_ij;
+ atom_id jj,jj0,jj1,i_atom;
+ int i0,nicg,len;
+
+ int *cginfo;
+ int *type,*typeB;
+ real *charge,*chargeB;
+ real qi,qiB,qq,rlj;
+ gmx_bool bFreeEnergy,bFree,bFreeJ,bNotEx,*bPert;
+ gmx_bool bDoVdW_i,bDoCoul_i,bDoCoul_i_sol;
+ int iwater,jwater;
+ t_nblist *nlist;
+
+ /* Copy some pointers */
+ cginfo = fr->cginfo;
+ charge = md->chargeA;
+ chargeB = md->chargeB;
+ type = md->typeA;
+ typeB = md->typeB;
+ bPert = md->bPerturbed;
+
+ /* Get atom range */
+ i0 = index[icg];
+ nicg = index[icg+1]-i0;
+
+ /* Get the i charge group info */
+ igid = GET_CGINFO_GID(cginfo[icg]);
+ iwater = GET_CGINFO_SOLOPT(cginfo[icg]);
+
+ bFreeEnergy = FALSE;
+ if (md->nPerturbed)
+ {
+ /* Check if any of the particles involved are perturbed.
+ * If not we can do the cheaper normal put_in_list
+ * and use more solvent optimization.
+ */
+ for(i=0; i<nicg; i++)
+ {
+ bFreeEnergy |= bPert[i0+i];
+ }
+ /* Loop over the j charge groups */
+ for(j=0; (j<nj && !bFreeEnergy); j++)
+ {
+ jcg = jjcg[j];
+ jj0 = index[jcg];
+ jj1 = index[jcg+1];
+ /* Finally loop over the atoms in the j-charge group */
+ for(jj=jj0; jj<jj1; jj++)
+ {
+ bFreeEnergy |= bPert[jj];
+ }
+ }
+ }
+
+ /* Unpack pointers to neighbourlist structs */
+ if (fr->nnblists == 1)
+ {
+ nbl_ind = 0;
+ }
+ else
+ {
+ nbl_ind = fr->gid2nblists[GID(igid,jgid,ngid)];
+ }
+ if (bLR)
+ {
+ nlist = fr->nblists[nbl_ind].nlist_lr;
+ }
+ else
+ {
+ nlist = fr->nblists[nbl_ind].nlist_sr;
+ }
+
+ if (iwater != esolNO)
+ {
+ vdwc = &nlist[eNL_VDWQQ_WATER];
+ vdw = &nlist[eNL_VDW];
+ coul = &nlist[eNL_QQ_WATER];
+#ifndef DISABLE_WATERWATER_NLIST
+ vdwc_ww = &nlist[eNL_VDWQQ_WATERWATER];
+ coul_ww = &nlist[eNL_QQ_WATERWATER];
+#endif
+ }
+ else
+ {
+ vdwc = &nlist[eNL_VDWQQ];
+ vdw = &nlist[eNL_VDW];
+ coul = &nlist[eNL_QQ];
+ }
+
+ if (!bFreeEnergy)
+ {
+ if (iwater != esolNO)
+ {
+ /* Loop over the atoms in the i charge group */
+ i_atom = i0;
+ gid = GID(igid,jgid,ngid);
+ /* Create new i_atom for each energy group */
+ if (bDoCoul && bDoVdW)
+ {
+ new_i_nblist(vdwc,bLR,i_atom,shift,gid);
+#ifndef DISABLE_WATERWATER_NLIST
+ new_i_nblist(vdwc_ww,bLR,i_atom,shift,gid);
+#endif
+ }
+ if (bDoVdW)
+ {
+ new_i_nblist(vdw,bLR,i_atom,shift,gid);
+ }
+ if (bDoCoul)
+ {
+ new_i_nblist(coul,bLR,i_atom,shift,gid);
+#ifndef DISABLE_WATERWATER_NLIST
+ new_i_nblist(coul_ww,bLR,i_atom,shift,gid);
+#endif
+ }
+ /* Loop over the j charge groups */
+ for(j=0; (j<nj); j++)
+ {
+ jcg=jjcg[j];
+
+ if (jcg == icg)
+ {
+ continue;
+ }
+
+ jj0 = index[jcg];
+ jwater = GET_CGINFO_SOLOPT(cginfo[jcg]);
+
+ if (iwater == esolSPC && jwater == esolSPC)
+ {
+ /* Interaction between two SPC molecules */
+ if (!bDoCoul)
+ {
+ /* VdW only - only first atoms in each water interact */
+ add_j_to_nblist(vdw,jj0,bLR);
+ }
+ else
+ {
+#ifdef DISABLE_WATERWATER_NLIST
+ /* Add entries for the three atoms - only do VdW if we need to */
+ if (!bDoVdW)
+ {
+ add_j_to_nblist(coul,jj0,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdwc,jj0,bLR);
+ }
+ add_j_to_nblist(coul,jj0+1,bLR);
+ add_j_to_nblist(coul,jj0+2,bLR);
+#else
+ /* One entry for the entire water-water interaction */
+ if (!bDoVdW)
+ {
+ add_j_to_nblist(coul_ww,jj0,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdwc_ww,jj0,bLR);
+ }
+#endif
+ }
+ }
+ else if (iwater == esolTIP4P && jwater == esolTIP4P)
+ {
+ /* Interaction between two TIP4p molecules */
+ if (!bDoCoul)
+ {
+ /* VdW only - only first atoms in each water interact */
+ add_j_to_nblist(vdw,jj0,bLR);
+ }
+ else
+ {
+#ifdef DISABLE_WATERWATER_NLIST
+ /* Add entries for the four atoms - only do VdW if we need to */
+ if (bDoVdW)
+ {
+ add_j_to_nblist(vdw,jj0,bLR);
+ }
+ add_j_to_nblist(coul,jj0+1,bLR);
+ add_j_to_nblist(coul,jj0+2,bLR);
+ add_j_to_nblist(coul,jj0+3,bLR);
+#else
+ /* One entry for the entire water-water interaction */
+ if (!bDoVdW)
+ {
+ add_j_to_nblist(coul_ww,jj0,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdwc_ww,jj0,bLR);
+ }
+#endif
+ }
+ }
+ else
+ {
+ /* j charge group is not water, but i is.
+ * Add entries to the water-other_atom lists; the geometry of the water
+ * molecule doesn't matter - that is taken care of in the nonbonded kernel,
+ * so we don't care if it is SPC or TIP4P...
+ */
+
+ jj1 = index[jcg+1];
+
+ if (!bDoVdW)
+ {
+ for(jj=jj0; (jj<jj1); jj++)
+ {
+ if (charge[jj] != 0)
+ {
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ }
+ else if (!bDoCoul)
+ {
+ for(jj=jj0; (jj<jj1); jj++)
+ {
+ if (bHaveVdW[type[jj]])
+ {
+ add_j_to_nblist(vdw,jj,bLR);
+ }
+ }
+ }
+ else
+ {
+ /* _charge_ _groups_ interact with both coulomb and LJ */
+ /* Check which atoms we should add to the lists! */
+ for(jj=jj0; (jj<jj1); jj++)
+ {
+ if (bHaveVdW[type[jj]])
+ {
+ if (charge[jj] != 0)
+ {
+ add_j_to_nblist(vdwc,jj,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdw,jj,bLR);
+ }
+ }
+ else if (charge[jj] != 0)
+ {
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ }
+ }
+ }
+ close_i_nblist(vdw);
+ close_i_nblist(coul);
+ close_i_nblist(vdwc);
+#ifndef DISABLE_WATERWATER_NLIST
+ close_i_nblist(coul_ww);
+ close_i_nblist(vdwc_ww);
+#endif
+ }
+ else
+ {
+ /* no solvent as i charge group */
+ /* Loop over the atoms in the i charge group */
+ for(i=0; i<nicg; i++)
+ {
+ i_atom = i0+i;
+ gid = GID(igid,jgid,ngid);
+ qi = charge[i_atom];
+
+ /* Create new i_atom for each energy group */
+ if (bDoVdW && bDoCoul)
+ {
+ new_i_nblist(vdwc,bLR,i_atom,shift,gid);
+ }
+ if (bDoVdW)
+ {
+ new_i_nblist(vdw,bLR,i_atom,shift,gid);
+ }
+ if (bDoCoul)
+ {
+ new_i_nblist(coul,bLR,i_atom,shift,gid);
+ }
+ bDoVdW_i = (bDoVdW && bHaveVdW[type[i_atom]]);
+ bDoCoul_i = (bDoCoul && qi!=0);
+
+ if (bDoVdW_i || bDoCoul_i)
+ {
+ /* Loop over the j charge groups */
+ for(j=0; (j<nj); j++)
+ {
+ jcg=jjcg[j];
+
+ /* Check for large charge groups */
+ if (jcg == icg)
+ {
+ jj0 = i0 + i + 1;
+ }
+ else
+ {
+ jj0 = index[jcg];
+ }
+
+ jj1=index[jcg+1];
+ /* Finally loop over the atoms in the j-charge group */
+ for(jj=jj0; jj<jj1; jj++)
+ {
+ bNotEx = NOTEXCL(bExcl,i,jj);
+
+ if (bNotEx)
+ {
+ if (!bDoVdW_i)
+ {
+ if (charge[jj] != 0)
+ {
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ else if (!bDoCoul_i)
+ {
+ if (bHaveVdW[type[jj]])
+ {
+ add_j_to_nblist(vdw,jj,bLR);
+ }
+ }
+ else
+ {
+ if (bHaveVdW[type[jj]])
+ {
+ if (charge[jj] != 0)
+ {
+ add_j_to_nblist(vdwc,jj,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdw,jj,bLR);
+ }
+ }
+ else if (charge[jj] != 0)
+ {
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ }
+ }
+ }
+ }
+ close_i_nblist(vdw);
+ close_i_nblist(coul);
+ close_i_nblist(vdwc);
+ }
+ }
+ }
+ else
+ {
+ /* we are doing free energy */
+ vdwc_free = &nlist[eNL_VDWQQ_FREE];
+ vdw_free = &nlist[eNL_VDW_FREE];
+ coul_free = &nlist[eNL_QQ_FREE];
+ /* Loop over the atoms in the i charge group */
+ for(i=0; i<nicg; i++)
+ {
+ i_atom = i0+i;
+ gid = GID(igid,jgid,ngid);
+ qi = charge[i_atom];
+ qiB = chargeB[i_atom];
+
+ /* Create new i_atom for each energy group */
+ if (bDoVdW && bDoCoul)
+ new_i_nblist(vdwc,bLR,i_atom,shift,gid);
+ if (bDoVdW)
+ new_i_nblist(vdw,bLR,i_atom,shift,gid);
+ if (bDoCoul)
+ new_i_nblist(coul,bLR,i_atom,shift,gid);
+
+ new_i_nblist(vdw_free,bLR,i_atom,shift,gid);
+ new_i_nblist(coul_free,bLR,i_atom,shift,gid);
+ new_i_nblist(vdwc_free,bLR,i_atom,shift,gid);
+
+ bDoVdW_i = (bDoVdW &&
+ (bHaveVdW[type[i_atom]] || bHaveVdW[typeB[i_atom]]));
+ bDoCoul_i = (bDoCoul && (qi!=0 || qiB!=0));
+ /* For TIP4P the first atom does not have a charge,
+ * but the last three do. So we should still put an atom
+ * without LJ but with charge in the water-atom neighborlist
+ * for a TIP4p i charge group.
+ * For SPC type water the first atom has LJ and charge,
+ * so there is no such problem.
+ */
+ if (iwater == esolNO)
+ {
+ bDoCoul_i_sol = bDoCoul_i;
+ }
+ else
+ {
+ bDoCoul_i_sol = bDoCoul;
+ }
+
+ if (bDoVdW_i || bDoCoul_i_sol)
+ {
+ /* Loop over the j charge groups */
+ for(j=0; (j<nj); j++)
+ {
+ jcg=jjcg[j];
+
+ /* Check for large charge groups */
+ if (jcg == icg)
+ {
+ jj0 = i0 + i + 1;
+ }
+ else
+ {
+ jj0 = index[jcg];
+ }
+
+ jj1=index[jcg+1];
+ /* Finally loop over the atoms in the j-charge group */
+ bFree = bPert[i_atom];
+ for(jj=jj0; (jj<jj1); jj++)
+ {
+ bFreeJ = bFree || bPert[jj];
+ /* Complicated if, because the water H's should also
+ * see perturbed j-particles
+ */
+ if (iwater==esolNO || i==0 || bFreeJ)
+ {
+ bNotEx = NOTEXCL(bExcl,i,jj);
+
+ if (bNotEx)
+ {
+ if (bFreeJ)
+ {
+ if (!bDoVdW_i)
+ {
+ if (charge[jj]!=0 || chargeB[jj]!=0)
+ {
+ add_j_to_nblist(coul_free,jj,bLR);
+ }
+ }
+ else if (!bDoCoul_i)
+ {
+ if (bHaveVdW[type[jj]] || bHaveVdW[typeB[jj]])
+ {
+ add_j_to_nblist(vdw_free,jj,bLR);
+ }
+ }
+ else
+ {
+ if (bHaveVdW[type[jj]] || bHaveVdW[typeB[jj]])
+ {
+ if (charge[jj]!=0 || chargeB[jj]!=0)
+ {
+ add_j_to_nblist(vdwc_free,jj,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdw_free,jj,bLR);
+ }
+ }
+ else if (charge[jj]!=0 || chargeB[jj]!=0)
+ add_j_to_nblist(coul_free,jj,bLR);
+ }
+ }
+ else if (!bDoVdW_i)
+ {
+ /* This is done whether or not bWater is set */
+ if (charge[jj] != 0)
+ {
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ else if (!bDoCoul_i_sol)
+ {
+ if (bHaveVdW[type[jj]])
+ {
+ add_j_to_nblist(vdw,jj,bLR);
+ }
+ }
+ else
+ {
+ if (bHaveVdW[type[jj]])
+ {
+ if (charge[jj] != 0)
+ {
+ add_j_to_nblist(vdwc,jj,bLR);
+ }
+ else
+ {
+ add_j_to_nblist(vdw,jj,bLR);
+ }
+ }
+ else if (charge[jj] != 0)
+ {
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ close_i_nblist(vdw);
+ close_i_nblist(coul);
+ close_i_nblist(vdwc);
+ close_i_nblist(vdw_free);
+ close_i_nblist(coul_free);
+ close_i_nblist(vdwc_free);
+ }
+ }
+}
+
+static void
+put_in_list_qmmm(gmx_bool bHaveVdW[],
+ int ngid,
+ t_mdatoms * md,
+ int icg,
+ int jgid,
+ int nj,
+ atom_id jjcg[],
+ atom_id index[],
+ t_excl bExcl[],
+ int shift,
+ t_forcerec * fr,
+ gmx_bool bLR,
+ gmx_bool bDoVdW,
+ gmx_bool bDoCoul)
+{
+ t_nblist * coul;
+ int i,j,jcg,igid,gid;
+ atom_id jj,jj0,jj1,i_atom;
+ int i0,nicg;
+ gmx_bool bNotEx;
+
+ /* Get atom range */
+ i0 = index[icg];
+ nicg = index[icg+1]-i0;
+
+ /* Get the i charge group info */
+ igid = GET_CGINFO_GID(fr->cginfo[icg]);
+
+ coul = &fr->QMMMlist;
+
+ /* Loop over atoms in the ith charge group */
+ for (i=0;i<nicg;i++)
+ {
+ i_atom = i0+i;
+ gid = GID(igid,jgid,ngid);
+ /* Create new i_atom for each energy group */
+ new_i_nblist(coul,bLR,i_atom,shift,gid);
+
+ /* Loop over the j charge groups */
+ for (j=0;j<nj;j++)
+ {
+ jcg=jjcg[j];
+
+ /* Charge groups cannot have QM and MM atoms simultaneously */
+ if (jcg!=icg)
+ {
+ jj0 = index[jcg];
+ jj1 = index[jcg+1];
+ /* Finally loop over the atoms in the j-charge group */
+ for(jj=jj0; jj<jj1; jj++)
+ {
+ bNotEx = NOTEXCL(bExcl,i,jj);
+ if(bNotEx)
+ add_j_to_nblist(coul,jj,bLR);
+ }
+ }
+ }
+ close_i_nblist(coul);
+ }
+}
+
+static void
+put_in_list_cg(gmx_bool bHaveVdW[],
+ int ngid,
+ t_mdatoms * md,
+ int icg,
+ int jgid,
+ int nj,
+ atom_id jjcg[],
+ atom_id index[],
+ t_excl bExcl[],
+ int shift,
+ t_forcerec * fr,
+ gmx_bool bLR,
+ gmx_bool bDoVdW,
+ gmx_bool bDoCoul)
+{
+ int cginfo;
+ int igid,gid,nbl_ind;
+ t_nblist * vdwc;
+ int j,jcg;
+
+ cginfo = fr->cginfo[icg];
+
+ igid = GET_CGINFO_GID(cginfo);
+ gid = GID(igid,jgid,ngid);
+
+ /* Unpack pointers to neighbourlist structs */
+ if (fr->nnblists == 1)
+ {
+ nbl_ind = 0;
+ }
+ else
+ {
+ nbl_ind = fr->gid2nblists[gid];
+ }
+ if (bLR)
+ {
+ vdwc = &fr->nblists[nbl_ind].nlist_lr[eNL_VDWQQ];
+ }
+ else
+ {
+ vdwc = &fr->nblists[nbl_ind].nlist_sr[eNL_VDWQQ];
+ }
+
+ /* Make a new neighbor list for charge group icg.
+ * Currently simply one neighbor list is made with LJ and Coulomb.
+ * If required, zero interactions could be removed here
+ * or in the force loop.
+ */
+ new_i_nblist(vdwc,bLR,index[icg],shift,gid);
+ vdwc->iinr_end[vdwc->nri] = index[icg+1];
+
+ for(j=0; (j<nj); j++)
+ {
+ jcg = jjcg[j];
+ /* Skip the icg-icg pairs if all self interactions are excluded */
+ if (!(jcg == icg && GET_CGINFO_EXCL_INTRA(cginfo)))
+ {
+ /* Here we add the j charge group jcg to the list,
+ * exclusions are also added to the list.
+ */
+ add_j_to_nblist_cg(vdwc,index[jcg],index[jcg+1],bExcl,bLR);
+ }
+ }
+
+ close_i_nblist(vdwc);
+}
+
+static void setexcl(atom_id start,atom_id end,t_blocka *excl,gmx_bool b,
+ t_excl bexcl[])
+{
+ atom_id i,k;
+
+ if (b)
+ {
+ for(i=start; i<end; i++)
+ {
+ for(k=excl->index[i]; k<excl->index[i+1]; k++)
+ {
+ SETEXCL(bexcl,i-start,excl->a[k]);
+ }
+ }
+ }
+ else
+ {
+ for(i=start; i<end; i++)
+ {
+ for(k=excl->index[i]; k<excl->index[i+1]; k++)
+ {
+ RMEXCL(bexcl,i-start,excl->a[k]);
+ }
+ }
+ }
+}
+
+int calc_naaj(int icg,int cgtot)
+{
+ int naaj;
+
+ if ((cgtot % 2) == 1)
+ {
+ /* Odd number of charge groups, easy */
+ naaj = 1 + (cgtot/2);
+ }
+ else if ((cgtot % 4) == 0)
+ {
+ /* Multiple of four is hard */
+ if (icg < cgtot/2)
+ {
+ if ((icg % 2) == 0)
+ {
+ naaj=1+(cgtot/2);
+ }
+ else
+ {
+ naaj=cgtot/2;
+ }
+ }
+ else
+ {
+ if ((icg % 2) == 1)
+ {
+ naaj=1+(cgtot/2);
+ }
+ else
+ {
+ naaj=cgtot/2;
+ }
+ }
+ }
+ else
+ {
+ /* cgtot/2 = odd */
+ if ((icg % 2) == 0)
+ {
+ naaj=1+(cgtot/2);
+ }
+ else
+ {
+ naaj=cgtot/2;
+ }
+ }
+#ifdef DEBUG
+ fprintf(log,"naaj=%d\n",naaj);
+#endif
+
+ return naaj;
+}
+
+/************************************************
+ *
+ * S I M P L E C O R E S T U F F
+ *
+ ************************************************/
+
+static real calc_image_tric(rvec xi,rvec xj,matrix box,
+ rvec b_inv,int *shift)
+{
+ /* This code assumes that the cut-off is smaller than
+ * a half times the smallest diagonal element of the box.
+ */
+ const real h25=2.5;
+ real dx,dy,dz;
+ real r2;
+ int tx,ty,tz;
+
+ /* Compute diff vector */
+ dz = xj[ZZ] - xi[ZZ];
+ dy = xj[YY] - xi[YY];
+ dx = xj[XX] - xi[XX];
+
+ /* Perform NINT operation, using trunc operation, therefore
+ * we first add 2.5 then subtract 2 again
+ */
+ tz = dz*b_inv[ZZ] + h25;
+ tz -= 2;
+ dz -= tz*box[ZZ][ZZ];
+ dy -= tz*box[ZZ][YY];
+ dx -= tz*box[ZZ][XX];
+
+ ty = dy*b_inv[YY] + h25;
+ ty -= 2;
+ dy -= ty*box[YY][YY];
+ dx -= ty*box[YY][XX];
+
+ tx = dx*b_inv[XX]+h25;
+ tx -= 2;
+ dx -= tx*box[XX][XX];
+
+ /* Distance squared */
+ r2 = (dx*dx) + (dy*dy) + (dz*dz);
+
+ *shift = XYZ2IS(tx,ty,tz);
+
+ return r2;
+}
+
+static real calc_image_rect(rvec xi,rvec xj,rvec box_size,
+ rvec b_inv,int *shift)
+{
+ const real h15=1.5;
+ real ddx,ddy,ddz;
+ real dx,dy,dz;
+ real r2;
+ int tx,ty,tz;
+
+ /* Compute diff vector */
+ dx = xj[XX] - xi[XX];
+ dy = xj[YY] - xi[YY];
+ dz = xj[ZZ] - xi[ZZ];
+
+ /* Perform NINT operation, using trunc operation, therefore
+ * we first add 1.5 then subtract 1 again
+ */
+ tx = dx*b_inv[XX] + h15;
+ ty = dy*b_inv[YY] + h15;
+ tz = dz*b_inv[ZZ] + h15;
+ tx--;
+ ty--;
+ tz--;
+
+ /* Correct diff vector for translation */
+ ddx = tx*box_size[XX] - dx;
+ ddy = ty*box_size[YY] - dy;
+ ddz = tz*box_size[ZZ] - dz;
+
+ /* Distance squared */
+ r2 = (ddx*ddx) + (ddy*ddy) + (ddz*ddz);
+
+ *shift = XYZ2IS(tx,ty,tz);
+
+ return r2;
+}
+
+static void add_simple(t_ns_buf *nsbuf,int nrj,atom_id cg_j,
+ gmx_bool bHaveVdW[],int ngid,t_mdatoms *md,
+ int icg,int jgid,t_block *cgs,t_excl bexcl[],
+ int shift,t_forcerec *fr,put_in_list_t *put_in_list)
+{
+ if (nsbuf->nj + nrj > MAX_CG)
+ {
+ put_in_list(bHaveVdW,ngid,md,icg,jgid,nsbuf->ncg,nsbuf->jcg,
+ cgs->index,bexcl,shift,fr,FALSE,TRUE,TRUE);
+ /* Reset buffer contents */
+ nsbuf->ncg = nsbuf->nj = 0;
+ }
+ nsbuf->jcg[nsbuf->ncg++] = cg_j;
+ nsbuf->nj += nrj;
+}
+
+static void ns_inner_tric(rvec x[],int icg,int *i_egp_flags,
+ int njcg,atom_id jcg[],
+ matrix box,rvec b_inv,real rcut2,
+ t_block *cgs,t_ns_buf **ns_buf,
+ gmx_bool bHaveVdW[],int ngid,t_mdatoms *md,
+ t_excl bexcl[],t_forcerec *fr,
+ put_in_list_t *put_in_list)
+{
+ int shift;
+ int j,nrj,jgid;
+ int *cginfo=fr->cginfo;
+ atom_id cg_j,*cgindex;
+ t_ns_buf *nsbuf;
+
+ cgindex = cgs->index;
+ shift = CENTRAL;
+ for(j=0; (j<njcg); j++)
+ {
+ cg_j = jcg[j];
+ nrj = cgindex[cg_j+1]-cgindex[cg_j];
+ if (calc_image_tric(x[icg],x[cg_j],box,b_inv,&shift) < rcut2)
+ {
+ jgid = GET_CGINFO_GID(cginfo[cg_j]);
+ if (!(i_egp_flags[jgid] & EGP_EXCL))
+ {
+ add_simple(&ns_buf[jgid][shift],nrj,cg_j,
+ bHaveVdW,ngid,md,icg,jgid,cgs,bexcl,shift,fr,
+ put_in_list);
+ }
+ }
+ }
+}
+
+static void ns_inner_rect(rvec x[],int icg,int *i_egp_flags,
+ int njcg,atom_id jcg[],
+ gmx_bool bBox,rvec box_size,rvec b_inv,real rcut2,
+ t_block *cgs,t_ns_buf **ns_buf,
+ gmx_bool bHaveVdW[],int ngid,t_mdatoms *md,
+ t_excl bexcl[],t_forcerec *fr,
+ put_in_list_t *put_in_list)
+{
+ int shift;
+ int j,nrj,jgid;
+ int *cginfo=fr->cginfo;
+ atom_id cg_j,*cgindex;
+ t_ns_buf *nsbuf;
+
+ cgindex = cgs->index;
+ if (bBox)
+ {
+ shift = CENTRAL;
+ for(j=0; (j<njcg); j++)
+ {
+ cg_j = jcg[j];
+ nrj = cgindex[cg_j+1]-cgindex[cg_j];
+ if (calc_image_rect(x[icg],x[cg_j],box_size,b_inv,&shift) < rcut2)
+ {
+ jgid = GET_CGINFO_GID(cginfo[cg_j]);
+ if (!(i_egp_flags[jgid] & EGP_EXCL))
+ {
+ add_simple(&ns_buf[jgid][shift],nrj,cg_j,
+ bHaveVdW,ngid,md,icg,jgid,cgs,bexcl,shift,fr,
+ put_in_list);
+ }
+ }
+ }
+ }
+ else
+ {
+ for(j=0; (j<njcg); j++)
+ {
+ cg_j = jcg[j];
+ nrj = cgindex[cg_j+1]-cgindex[cg_j];
+ if ((rcut2 == 0) || (distance2(x[icg],x[cg_j]) < rcut2)) {
+ jgid = GET_CGINFO_GID(cginfo[cg_j]);
+ if (!(i_egp_flags[jgid] & EGP_EXCL))
+ {
+ add_simple(&ns_buf[jgid][CENTRAL],nrj,cg_j,
+ bHaveVdW,ngid,md,icg,jgid,cgs,bexcl,CENTRAL,fr,
+ put_in_list);
+ }
+ }
+ }
+ }
+}
+
+/* ns_simple_core needs to be adapted for QMMM still 2005 */
+
+static int ns_simple_core(t_forcerec *fr,
+ gmx_localtop_t *top,
+ t_mdatoms *md,
+ matrix box,rvec box_size,
+ t_excl bexcl[],atom_id *aaj,
+ int ngid,t_ns_buf **ns_buf,
+ put_in_list_t *put_in_list,gmx_bool bHaveVdW[])
+{
+ int naaj,k;
+ real rlist2;
+ int nsearch,icg,jcg,igid,i0,nri,nn;
+ int *cginfo;
+ t_ns_buf *nsbuf;
+ /* atom_id *i_atoms; */
+ t_block *cgs=&(top->cgs);
+ t_blocka *excl=&(top->excls);
+ rvec b_inv;
+ int m;
+ gmx_bool bBox,bTriclinic;
+ int *i_egp_flags;
+
+ rlist2 = sqr(fr->rlist);
+
+ bBox = (fr->ePBC != epbcNONE);
+ if (bBox)
+ {
+ for(m=0; (m<DIM); m++)
+ {
+ b_inv[m] = divide_err(1.0,box_size[m]);
+ }
+ bTriclinic = TRICLINIC(box);
+ }
+ else
+ {
+ bTriclinic = FALSE;
+ }
+
+ cginfo = fr->cginfo;
+
+ nsearch=0;
+ for (icg=fr->cg0; (icg<fr->hcg); icg++)
+ {
+ /*
+ i0 = cgs->index[icg];
+ nri = cgs->index[icg+1]-i0;
+ i_atoms = &(cgs->a[i0]);
+ i_eg_excl = fr->eg_excl + ngid*md->cENER[*i_atoms];
+ setexcl(nri,i_atoms,excl,TRUE,bexcl);
+ */
+ igid = GET_CGINFO_GID(cginfo[icg]);
+ i_egp_flags = fr->egp_flags + ngid*igid;
+ setexcl(cgs->index[icg],cgs->index[icg+1],excl,TRUE,bexcl);
+
+ naaj=calc_naaj(icg,cgs->nr);
+ if (bTriclinic)
+ {
+ ns_inner_tric(fr->cg_cm,icg,i_egp_flags,naaj,&(aaj[icg]),
+ box,b_inv,rlist2,cgs,ns_buf,
+ bHaveVdW,ngid,md,bexcl,fr,put_in_list);
+ }
+ else
+ {
+ ns_inner_rect(fr->cg_cm,icg,i_egp_flags,naaj,&(aaj[icg]),
+ bBox,box_size,b_inv,rlist2,cgs,ns_buf,
+ bHaveVdW,ngid,md,bexcl,fr,put_in_list);
+ }
+ nsearch += naaj;
+
+ for(nn=0; (nn<ngid); nn++)
+ {
+ for(k=0; (k<SHIFTS); k++)
+ {
+ nsbuf = &(ns_buf[nn][k]);
+ if (nsbuf->ncg > 0)
+ {
+ put_in_list(bHaveVdW,ngid,md,icg,nn,nsbuf->ncg,nsbuf->jcg,
+ cgs->index,bexcl,k,fr,FALSE,TRUE,TRUE);
+ nsbuf->ncg=nsbuf->nj=0;
+ }
+ }
+ }
+ /* setexcl(nri,i_atoms,excl,FALSE,bexcl); */
+ setexcl(cgs->index[icg],cgs->index[icg+1],excl,FALSE,bexcl);
+ }
+ close_neighbor_list(fr,FALSE,-1,-1,FALSE);
+
+ return nsearch;
+}
+
+/************************************************
+ *
+ * N S 5 G R I D S T U F F
+ *
+ ************************************************/
+
+static inline void get_dx(int Nx,real gridx,real rc2,int xgi,real x,
+ int *dx0,int *dx1,real *dcx2)
+{
+ real dcx,tmp;
+ int xgi0,xgi1,i;
+
+ if (xgi < 0)
+ {
+ *dx0 = 0;
+ xgi0 = -1;
+ *dx1 = -1;
+ xgi1 = 0;
+ }
+ else if (xgi >= Nx)
+ {
+ *dx0 = Nx;
+ xgi0 = Nx-1;
+ *dx1 = Nx-1;
+ xgi1 = Nx;
+ }
+ else
+ {
+ dcx2[xgi] = 0;
+ *dx0 = xgi;
+ xgi0 = xgi-1;
+ *dx1 = xgi;
+ xgi1 = xgi+1;
+ }
+
+ for(i=xgi0; i>=0; i--)
+ {
+ dcx = (i+1)*gridx-x;
+ tmp = dcx*dcx;
+ if (tmp >= rc2)
+ break;
+ *dx0 = i;
+ dcx2[i] = tmp;
+ }
+ for(i=xgi1; i<Nx; i++)
+ {
+ dcx = i*gridx-x;
+ tmp = dcx*dcx;
+ if (tmp >= rc2)
+ {
+ break;
+ }
+ *dx1 = i;
+ dcx2[i] = tmp;
+ }
+}
+
+static inline void get_dx_dd(int Nx,real gridx,real rc2,int xgi,real x,
+ int ncpddc,int shift_min,int shift_max,
+ int *g0,int *g1,real *dcx2)
+{
+ real dcx,tmp;
+ int g_min,g_max,shift_home;
+
+ if (xgi < 0)
+ {
+ g_min = 0;
+ g_max = Nx - 1;
+ *g0 = 0;
+ *g1 = -1;
+ }
+ else if (xgi >= Nx)
+ {
+ g_min = 0;
+ g_max = Nx - 1;
+ *g0 = Nx;
+ *g1 = Nx - 1;
+ }
+ else
+ {
+ if (ncpddc == 0)
+ {
+ g_min = 0;
+ g_max = Nx - 1;
+ }
+ else
+ {
+ if (xgi < ncpddc)
+ {
+ shift_home = 0;
+ }
+ else
+ {
+ shift_home = -1;
+ }
+ g_min = (shift_min == shift_home ? 0 : ncpddc);
+ g_max = (shift_max == shift_home ? ncpddc - 1 : Nx - 1);
+ }
+ if (shift_min > 0)
+ {
+ *g0 = g_min;
+ *g1 = g_min - 1;
+ }
+ else if (shift_max < 0)
+ {
+ *g0 = g_max + 1;
+ *g1 = g_max;
+ }
+ else
+ {
+ *g0 = xgi;
+ *g1 = xgi;
+ dcx2[xgi] = 0;
+ }
+ }
+
+ while (*g0 > g_min)
+ {
+ /* Check one grid cell down */
+ dcx = ((*g0 - 1) + 1)*gridx - x;
+ tmp = dcx*dcx;
+ if (tmp >= rc2)
+ {
+ break;
+ }
+ (*g0)--;
+ dcx2[*g0] = tmp;
+ }
+
+ while (*g1 < g_max)
+ {
+ /* Check one grid cell up */
+ dcx = (*g1 + 1)*gridx - x;
+ tmp = dcx*dcx;
+ if (tmp >= rc2)
+ {
+ break;
+ }
+ (*g1)++;
+ dcx2[*g1] = tmp;
+ }
+}
+
+
+#define sqr(x) ((x)*(x))
+#define calc_dx2(XI,YI,ZI,y) (sqr(XI-y[XX]) + sqr(YI-y[YY]) + sqr(ZI-y[ZZ]))
+#define calc_cyl_dx2(XI,YI,y) (sqr(XI-y[XX]) + sqr(YI-y[YY]))
+/****************************************************
+ *
+ * F A S T N E I G H B O R S E A R C H I N G
+ *
+ * Optimized neighboursearching routine using grid
+ * at least 1x1x1, see GROMACS manual
+ *
+ ****************************************************/
+
+static void do_longrange(t_commrec *cr,gmx_localtop_t *top,t_forcerec *fr,
+ int ngid,t_mdatoms *md,int icg,
+ int jgid,int nlr,
+ atom_id lr[],t_excl bexcl[],int shift,
+ rvec x[],rvec box_size,t_nrnb *nrnb,
+ real lambda,real *dvdlambda,
+ gmx_grppairener_t *grppener,
+ gmx_bool bDoVdW,gmx_bool bDoCoul,
+ gmx_bool bEvaluateNow,put_in_list_t *put_in_list,
+ gmx_bool bHaveVdW[],
+ gmx_bool bDoForces,rvec *f)
+{
+ int n,i;
+ t_nblist *nl;
+
+ for(n=0; n<fr->nnblists; n++)
+ {
+ for(i=0; (i<eNL_NR); i++)
+ {
+ nl = &fr->nblists[n].nlist_lr[i];
+ if ((nl->nri > nl->maxnri-32) || bEvaluateNow)
+ {
+ close_neighbor_list(fr,TRUE,n,i,FALSE);
+ /* Evaluate the energies and forces */
+ do_nonbonded(cr,fr,x,f,md,NULL,
+ grppener->ener[fr->bBHAM ? egBHAMLR : egLJLR],
+ grppener->ener[egCOULLR],
+ grppener->ener[egGB],box_size,
+ nrnb,lambda,dvdlambda,n,i,
+ GMX_DONB_LR | GMX_DONB_FORCES);
+
+ reset_neighbor_list(fr,TRUE,n,i);
+ }
+ }
+ }
+
+ if (!bEvaluateNow)
+ {
+ /* Put the long range particles in a list */
+ /* do_longrange is never called for QMMM */
+ put_in_list(bHaveVdW,ngid,md,icg,jgid,nlr,lr,top->cgs.index,
+ bexcl,shift,fr,TRUE,bDoVdW,bDoCoul);
+ }
+}
+
+static void get_cutoff2(t_forcerec *fr,gmx_bool bDoLongRange,
+ real *rvdw2,real *rcoul2,
+ real *rs2,real *rm2,real *rl2)
+{
+ *rs2 = sqr(fr->rlist);
+ if (bDoLongRange && fr->bTwinRange)
+ {
+ /* The VdW and elec. LR cut-off's could be different,
+ * so we can not simply set them to rlistlong.
+ */
+ if (EVDW_MIGHT_BE_ZERO_AT_CUTOFF(fr->vdwtype) &&
+ fr->rvdw > fr->rlist)
+ {
+ *rvdw2 = sqr(fr->rlistlong);
+ }
+ else
+ {
+ *rvdw2 = sqr(fr->rvdw);
+ }
+ if (EEL_MIGHT_BE_ZERO_AT_CUTOFF(fr->eeltype) &&
+ fr->rcoulomb > fr->rlist)
+ {
+ *rcoul2 = sqr(fr->rlistlong);
+ }
+ else
+ {
+ *rcoul2 = sqr(fr->rcoulomb);
+ }
+ }
+ else
+ {
+ /* Workaround for a gcc -O3 or -ffast-math problem */
+ *rvdw2 = *rs2;
+ *rcoul2 = *rs2;
+ }
+ *rm2 = min(*rvdw2,*rcoul2);
+ *rl2 = max(*rvdw2,*rcoul2);
+}
+
+static void init_nsgrid_lists(t_forcerec *fr,int ngid,gmx_ns_t *ns)
+{
+ real rvdw2,rcoul2,rs2,rm2,rl2;
+ int j;
+
+ get_cutoff2(fr,TRUE,&rvdw2,&rcoul2,&rs2,&rm2,&rl2);
+
+ /* Short range buffers */
+ snew(ns->nl_sr,ngid);
+ /* Counters */
+ snew(ns->nsr,ngid);
+ snew(ns->nlr_ljc,ngid);
+ snew(ns->nlr_one,ngid);
+
+ if (rm2 > rs2)
+ {
+ /* Long range VdW and Coul buffers */
+ snew(ns->nl_lr_ljc,ngid);
+ }
+ if (rl2 > rm2)
+ {
+ /* Long range VdW or Coul only buffers */
+ snew(ns->nl_lr_one,ngid);
+ }
+ for(j=0; (j<ngid); j++) {
+ snew(ns->nl_sr[j],MAX_CG);
+ if (rm2 > rs2)
+ {
+ snew(ns->nl_lr_ljc[j],MAX_CG);
+ }
+ if (rl2 > rm2)
+ {
+ snew(ns->nl_lr_one[j],MAX_CG);
+ }
+ }
+ if (debug)
+ {
+ fprintf(debug,
+ "ns5_core: rs2 = %g, rm2 = %g, rl2 = %g (nm^2)\n",
+ rs2,rm2,rl2);
+ }
+}
+
+static int nsgrid_core(FILE *log,t_commrec *cr,t_forcerec *fr,
+ matrix box,rvec box_size,int ngid,
+ gmx_localtop_t *top,
+ t_grid *grid,rvec x[],
+ t_excl bexcl[],gmx_bool *bExcludeAlleg,
+ t_nrnb *nrnb,t_mdatoms *md,
+ real lambda,real *dvdlambda,
+ gmx_grppairener_t *grppener,
+ put_in_list_t *put_in_list,
+ gmx_bool bHaveVdW[],
+ gmx_bool bDoLongRange,gmx_bool bDoForces,rvec *f,
+ gmx_bool bMakeQMMMnblist)
+{
+ gmx_ns_t *ns;
+ atom_id **nl_lr_ljc,**nl_lr_one,**nl_sr;
+ int *nlr_ljc,*nlr_one,*nsr;
+ gmx_domdec_t *dd=NULL;
+ t_block *cgs=&(top->cgs);
+ int *cginfo=fr->cginfo;
+ /* atom_id *i_atoms,*cgsindex=cgs->index; */
+ ivec sh0,sh1,shp;
+ int cell_x,cell_y,cell_z;
+ int d,tx,ty,tz,dx,dy,dz,cj;
+#ifdef ALLOW_OFFDIAG_LT_HALFDIAG
+ int zsh_ty,zsh_tx,ysh_tx;
+#endif
+ int dx0,dx1,dy0,dy1,dz0,dz1;
+ int Nx,Ny,Nz,shift=-1,j,nrj,nns,nn=-1;
+ real gridx,gridy,gridz,grid_x,grid_y,grid_z;
+ real *dcx2,*dcy2,*dcz2;
+ int zgi,ygi,xgi;
+ int cg0,cg1,icg=-1,cgsnr,i0,igid,nri,naaj,max_jcg;
+ int jcg0,jcg1,jjcg,cgj0,jgid;
+ int *grida,*gridnra,*gridind;
+ gmx_bool rvdw_lt_rcoul,rcoul_lt_rvdw;
+ rvec xi,*cgcm,grid_offset;
+ real r2,rs2,rvdw2,rcoul2,rm2,rl2,XI,YI,ZI,dcx,dcy,dcz,tmp1,tmp2;
+ int *i_egp_flags;
+ gmx_bool bDomDec,bTriclinicX,bTriclinicY;
+ ivec ncpddc;
+
+ ns = &fr->ns;
+
+ bDomDec = DOMAINDECOMP(cr);
+ if (bDomDec)
+ {
+ dd = cr->dd;
+ }
+
+ bTriclinicX = ((YY < grid->npbcdim &&
+ (!bDomDec || dd->nc[YY]==1) && box[YY][XX] != 0) ||
+ (ZZ < grid->npbcdim &&
+ (!bDomDec || dd->nc[ZZ]==1) && box[ZZ][XX] != 0));
+ bTriclinicY = (ZZ < grid->npbcdim &&
+ (!bDomDec || dd->nc[ZZ]==1) && box[ZZ][YY] != 0);
+
+ cgsnr = cgs->nr;
+
+ get_cutoff2(fr,bDoLongRange,&rvdw2,&rcoul2,&rs2,&rm2,&rl2);
+
+ rvdw_lt_rcoul = (rvdw2 >= rcoul2);
+ rcoul_lt_rvdw = (rcoul2 >= rvdw2);
+
+ if (bMakeQMMMnblist)
+ {
+ rm2 = rl2;
+ rs2 = rl2;
+ }
+
+ nl_sr = ns->nl_sr;
+ nsr = ns->nsr;
+ nl_lr_ljc = ns->nl_lr_ljc;
+ nl_lr_one = ns->nl_lr_one;
+ nlr_ljc = ns->nlr_ljc;
+ nlr_one = ns->nlr_one;
+
+ /* Unpack arrays */
+ cgcm = fr->cg_cm;
+ Nx = grid->n[XX];
+ Ny = grid->n[YY];
+ Nz = grid->n[ZZ];
+ grida = grid->a;
+ gridind = grid->index;
+ gridnra = grid->nra;
+ nns = 0;
+
+ gridx = grid->cell_size[XX];
+ gridy = grid->cell_size[YY];
+ gridz = grid->cell_size[ZZ];
+ grid_x = 1/gridx;
+ grid_y = 1/gridy;
+ grid_z = 1/gridz;
+ copy_rvec(grid->cell_offset,grid_offset);
+ copy_ivec(grid->ncpddc,ncpddc);
+ dcx2 = grid->dcx2;
+ dcy2 = grid->dcy2;
+ dcz2 = grid->dcz2;
+
+#ifdef ALLOW_OFFDIAG_LT_HALFDIAG
+ zsh_ty = floor(-box[ZZ][YY]/box[YY][YY]+0.5);
+ zsh_tx = floor(-box[ZZ][XX]/box[XX][XX]+0.5);
+ ysh_tx = floor(-box[YY][XX]/box[XX][XX]+0.5);
+ if (zsh_tx!=0 && ysh_tx!=0)
+ {
+ /* This could happen due to rounding, when both ratios are 0.5 */
+ ysh_tx = 0;
+ }
+#endif
+
+ debug_gmx();
+
+ if (fr->n_tpi)
+ {
+ /* We only want a list for the test particle */
+ cg0 = cgsnr - 1;
+ }
+ else
+ {
+ cg0 = grid->icg0;
+ }
+ cg1 = grid->icg1;
+
+ /* Set the shift range */
+ for(d=0; d<DIM; d++)
+ {
+ sh0[d] = -1;
+ sh1[d] = 1;
+ /* Check if we need periodicity shifts.
+ * Without PBC or with domain decomposition we don't need them.
+ */
+ if (d >= ePBC2npbcdim(fr->ePBC) || (bDomDec && dd->nc[d] > 1))
+ {
+ shp[d] = 0;
+ }
+ else
+ {
+ if (d == XX &&
+ box[XX][XX] - fabs(box[YY][XX]) - fabs(box[ZZ][XX]) < sqrt(rl2))
+ {
+ shp[d] = 2;
+ }
+ else
+ {
+ shp[d] = 1;
+ }
+ }
+ }
+
+ /* Loop over charge groups */
+ for(icg=cg0; (icg < cg1); icg++)
+ {
+ igid = GET_CGINFO_GID(cginfo[icg]);
+ /* Skip this charge group if all energy groups are excluded! */
+ if (bExcludeAlleg[igid])
+ {
+ continue;
+ }
+
+ i0 = cgs->index[icg];
+
+ if (bMakeQMMMnblist)
+ {
+ /* Skip this charge group if it is not a QM atom while making a
+ * QM/MM neighbourlist
+ */
+ if (md->bQM[i0]==FALSE)
+ {
+ continue; /* MM particle, go to next particle */
+ }
+
+ /* Compute the number of charge groups that fall within the control
+ * of this one (icg)
+ */
+ naaj = calc_naaj(icg,cgsnr);
+ jcg0 = icg;
+ jcg1 = icg + naaj;
+ max_jcg = cgsnr;
+ }
+ else
+ {
+ /* make a normal neighbourlist */
+
+ if (bDomDec)
+ {
+ /* Get the j charge-group and dd cell shift ranges */
+ dd_get_ns_ranges(cr->dd,icg,&jcg0,&jcg1,sh0,sh1);
+ max_jcg = 0;
+ }
+ else
+ {
+ /* Compute the number of charge groups that fall within the control
+ * of this one (icg)
+ */
+ naaj = calc_naaj(icg,cgsnr);
+ jcg0 = icg;
+ jcg1 = icg + naaj;
+
+ if (fr->n_tpi)
+ {
+ /* The i-particle is awlways the test particle,
+ * so we want all j-particles
+ */
+ max_jcg = cgsnr - 1;
+ }
+ else
+ {
+ max_jcg = jcg1 - cgsnr;
+ }
+ }
+ }
+
+ i_egp_flags = fr->egp_flags + igid*ngid;
+
+ /* Set the exclusions for the atoms in charge group icg using a bitmask */
+ setexcl(i0,cgs->index[icg+1],&top->excls,TRUE,bexcl);
+
+ ci2xyz(grid,icg,&cell_x,&cell_y,&cell_z);
+
+ /* Changed iicg to icg, DvdS 990115
+ * (but see consistency check above, DvdS 990330)
+ */
+#ifdef NS5DB
+ fprintf(log,"icg=%5d, naaj=%5d, cell %d %d %d\n",
+ icg,naaj,cell_x,cell_y,cell_z);
+#endif
+ /* Loop over shift vectors in three dimensions */
+ for (tz=-shp[ZZ]; tz<=shp[ZZ]; tz++)
+ {
+ ZI = cgcm[icg][ZZ]+tz*box[ZZ][ZZ];
+ /* Calculate range of cells in Z direction that have the shift tz */
+ zgi = cell_z + tz*Nz;
+#define FAST_DD_NS
+#ifndef FAST_DD_NS
+ get_dx(Nz,gridz,rl2,zgi,ZI,&dz0,&dz1,dcz2);
+#else
+ get_dx_dd(Nz,gridz,rl2,zgi,ZI-grid_offset[ZZ],
+ ncpddc[ZZ],sh0[ZZ],sh1[ZZ],&dz0,&dz1,dcz2);
+#endif
+ if (dz0 > dz1)
+ {
+ continue;
+ }
+ for (ty=-shp[YY]; ty<=shp[YY]; ty++)
+ {
+ YI = cgcm[icg][YY]+ty*box[YY][YY]+tz*box[ZZ][YY];
+ /* Calculate range of cells in Y direction that have the shift ty */
+ if (bTriclinicY)
+ {
+ ygi = (int)(Ny + (YI - grid_offset[YY])*grid_y) - Ny;
+ }
+ else
+ {
+ ygi = cell_y + ty*Ny;
+ }
+#ifndef FAST_DD_NS
+ get_dx(Ny,gridy,rl2,ygi,YI,&dy0,&dy1,dcy2);
+#else
+ get_dx_dd(Ny,gridy,rl2,ygi,YI-grid_offset[YY],
+ ncpddc[YY],sh0[YY],sh1[YY],&dy0,&dy1,dcy2);
+#endif
+ if (dy0 > dy1)
+ {
+ continue;
+ }
+ for (tx=-shp[XX]; tx<=shp[XX]; tx++)
+ {
+ XI = cgcm[icg][XX]+tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
+ /* Calculate range of cells in X direction that have the shift tx */
+ if (bTriclinicX)
+ {
+ xgi = (int)(Nx + (XI - grid_offset[XX])*grid_x) - Nx;
+ }
+ else
+ {
+ xgi = cell_x + tx*Nx;
+ }
+#ifndef FAST_DD_NS
+ get_dx(Nx,gridx,rl2,xgi*Nx,XI,&dx0,&dx1,dcx2);
+#else
+ get_dx_dd(Nx,gridx,rl2,xgi,XI-grid_offset[XX],
+ ncpddc[XX],sh0[XX],sh1[XX],&dx0,&dx1,dcx2);
+#endif
+ if (dx0 > dx1)
+ {
+ continue;
+ }
+ /* Get shift vector */
+ shift=XYZ2IS(tx,ty,tz);
+#ifdef NS5DB
+ range_check(shift,0,SHIFTS);
+#endif
+ for(nn=0; (nn<ngid); nn++)
+ {
+ nsr[nn] = 0;
+ nlr_ljc[nn] = 0;
+ nlr_one[nn] = 0;
+ }
+#ifdef NS5DB
+ fprintf(log,"shift: %2d, dx0,1: %2d,%2d, dy0,1: %2d,%2d, dz0,1: %2d,%2d\n",
+ shift,dx0,dx1,dy0,dy1,dz0,dz1);
+ fprintf(log,"cgcm: %8.3f %8.3f %8.3f\n",cgcm[icg][XX],
+ cgcm[icg][YY],cgcm[icg][ZZ]);
+ fprintf(log,"xi: %8.3f %8.3f %8.3f\n",XI,YI,ZI);
+#endif
+ for (dx=dx0; (dx<=dx1); dx++)
+ {
+ tmp1 = rl2 - dcx2[dx];
+ for (dy=dy0; (dy<=dy1); dy++)
+ {
+ tmp2 = tmp1 - dcy2[dy];
+ if (tmp2 > 0)
+ {
+ for (dz=dz0; (dz<=dz1); dz++) {
+ if (tmp2 > dcz2[dz]) {
+ /* Find grid-cell cj in which possible neighbours are */
+ cj = xyz2ci(Ny,Nz,dx,dy,dz);
+
+ /* Check out how many cgs (nrj) there in this cell */
+ nrj = gridnra[cj];
+
+ /* Find the offset in the cg list */
+ cgj0 = gridind[cj];
+
+ /* Check if all j's are out of range so we
+ * can skip the whole cell.
+ * Should save some time, especially with DD.
+ */
+ if (nrj == 0 ||
+ (grida[cgj0] >= max_jcg &&
+ (grida[cgj0] >= jcg1 || grida[cgj0+nrj-1] < jcg0)))
+ {
+ continue;
+ }
+
+ /* Loop over cgs */
+ for (j=0; (j<nrj); j++)
+ {
+ jjcg = grida[cgj0+j];
+
+ /* check whether this guy is in range! */
+ if ((jjcg >= jcg0 && jjcg < jcg1) ||
+ (jjcg < max_jcg))
+ {
+ r2=calc_dx2(XI,YI,ZI,cgcm[jjcg]);
+ if (r2 < rl2) {
+ /* jgid = gid[cgsatoms[cgsindex[jjcg]]]; */
+ jgid = GET_CGINFO_GID(cginfo[jjcg]);
+ /* check energy group exclusions */
+ if (!(i_egp_flags[jgid] & EGP_EXCL))
+ {
+ if (r2 < rs2)
+ {
+ if (nsr[jgid] >= MAX_CG)
+ {
+ put_in_list(bHaveVdW,ngid,md,icg,jgid,
+ nsr[jgid],nl_sr[jgid],
+ cgs->index,/* cgsatoms, */ bexcl,
+ shift,fr,FALSE,TRUE,TRUE);
+ nsr[jgid]=0;
+ }
+ nl_sr[jgid][nsr[jgid]++]=jjcg;
+ }
+ else if (r2 < rm2)
+ {
+ if (nlr_ljc[jgid] >= MAX_CG)
+ {
+ do_longrange(cr,top,fr,ngid,md,icg,jgid,
+ nlr_ljc[jgid],
+ nl_lr_ljc[jgid],bexcl,shift,x,
+ box_size,nrnb,
+ lambda,dvdlambda,
+ grppener,
+ TRUE,TRUE,FALSE,
+ put_in_list,
+ bHaveVdW,
+ bDoForces,f);
+ nlr_ljc[jgid]=0;
+ }
+ nl_lr_ljc[jgid][nlr_ljc[jgid]++]=jjcg;
+ }
+ else
+ {
+ if (nlr_one[jgid] >= MAX_CG) {
+ do_longrange(cr,top,fr,ngid,md,icg,jgid,
+ nlr_one[jgid],
+ nl_lr_one[jgid],bexcl,shift,x,
+ box_size,nrnb,
+ lambda,dvdlambda,
+ grppener,
+ rvdw_lt_rcoul,rcoul_lt_rvdw,FALSE,
+ put_in_list,
+ bHaveVdW,
+ bDoForces,f);
+ nlr_one[jgid]=0;
+ }
+ nl_lr_one[jgid][nlr_one[jgid]++]=jjcg;
+ }
+ }
+ }
+ nns++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ /* CHECK whether there is anything left in the buffers */
+ for(nn=0; (nn<ngid); nn++)
+ {
+ if (nsr[nn] > 0)
+ {
+ put_in_list(bHaveVdW,ngid,md,icg,nn,nsr[nn],nl_sr[nn],
+ cgs->index, /* cgsatoms, */ bexcl,
+ shift,fr,FALSE,TRUE,TRUE);
+ }
+
+ if (nlr_ljc[nn] > 0)
+ {
+ do_longrange(cr,top,fr,ngid,md,icg,nn,nlr_ljc[nn],
+ nl_lr_ljc[nn],bexcl,shift,x,box_size,nrnb,
+ lambda,dvdlambda,grppener,TRUE,TRUE,FALSE,
+ put_in_list,bHaveVdW,bDoForces,f);
+ }
+
+ if (nlr_one[nn] > 0)
+ {
+ do_longrange(cr,top,fr,ngid,md,icg,nn,nlr_one[nn],
+ nl_lr_one[nn],bexcl,shift,x,box_size,nrnb,
+ lambda,dvdlambda,grppener,
+ rvdw_lt_rcoul,rcoul_lt_rvdw,FALSE,
+ put_in_list,bHaveVdW,bDoForces,f);
+ }
+ }
+ }
+ }
+ }
+ /* setexcl(nri,i_atoms,&top->atoms.excl,FALSE,bexcl); */
+ setexcl(cgs->index[icg],cgs->index[icg+1],&top->excls,FALSE,bexcl);
+ }
+ /* Perform any left over force calculations */
+ for (nn=0; (nn<ngid); nn++)
+ {
+ if (rm2 > rs2)
+ {
+ do_longrange(cr,top,fr,0,md,icg,nn,nlr_ljc[nn],
+ nl_lr_ljc[nn],bexcl,shift,x,box_size,nrnb,
+ lambda,dvdlambda,grppener,
+ TRUE,TRUE,TRUE,put_in_list,bHaveVdW,bDoForces,f);
+ }
+ if (rl2 > rm2) {
+ do_longrange(cr,top,fr,0,md,icg,nn,nlr_one[nn],
+ nl_lr_one[nn],bexcl,shift,x,box_size,nrnb,
+ lambda,dvdlambda,grppener,
+ rvdw_lt_rcoul,rcoul_lt_rvdw,
+ TRUE,put_in_list,bHaveVdW,bDoForces,f);
+ }
+ }
+ debug_gmx();
+
+ /* Close off short range neighbourlists */
+ close_neighbor_list(fr,FALSE,-1,-1,bMakeQMMMnblist);
+
+ return nns;
+}
+
+void ns_realloc_natoms(gmx_ns_t *ns,int natoms)
+{
+ int i;
+
+ if (natoms > ns->nra_alloc)
+ {
+ ns->nra_alloc = over_alloc_dd(natoms);
+ srenew(ns->bexcl,ns->nra_alloc);
+ for(i=0; i<ns->nra_alloc; i++)
+ {
+ ns->bexcl[i] = 0;
+ }
+ }
+}
+
+void init_ns(FILE *fplog,const t_commrec *cr,
+ gmx_ns_t *ns,t_forcerec *fr,
+ const gmx_mtop_t *mtop,
+ matrix box)
+{
+ int mt,icg,nr_in_cg,maxcg,i,j,jcg,ngid,ncg;
+ t_block *cgs;
+ char *ptr;
+
+ /* Compute largest charge groups size (# atoms) */
+ nr_in_cg=1;
+ for(mt=0; mt<mtop->nmoltype; mt++) {
+ cgs = &mtop->moltype[mt].cgs;
+ for (icg=0; (icg < cgs->nr); icg++)
+ {
+ nr_in_cg=max(nr_in_cg,(int)(cgs->index[icg+1]-cgs->index[icg]));
+ }
+ }
+
+ /* Verify whether largest charge group is <= max cg.
+ * This is determined by the type of the local exclusion type
+ * Exclusions are stored in bits. (If the type is not large
+ * enough, enlarge it, unsigned char -> unsigned short -> unsigned long)
+ */
+ maxcg = sizeof(t_excl)*8;
+ if (nr_in_cg > maxcg)
+ {
+ gmx_fatal(FARGS,"Max #atoms in a charge group: %d > %d\n",
+ nr_in_cg,maxcg);
+ }
+
+ ngid = mtop->groups.grps[egcENER].nr;
+ snew(ns->bExcludeAlleg,ngid);
+ for(i=0; i<ngid; i++) {
+ ns->bExcludeAlleg[i] = TRUE;
+ for(j=0; j<ngid; j++)
+ {
+ if (!(fr->egp_flags[i*ngid+j] & EGP_EXCL))
+ {
+ ns->bExcludeAlleg[i] = FALSE;
+ }
+ }
+ }
+
+ if (fr->bGrid) {
+ /* Grid search */
+ ns->grid = init_grid(fplog,fr);
+ init_nsgrid_lists(fr,ngid,ns);
+ }
+ else
+ {
+ /* Simple search */
+ snew(ns->ns_buf,ngid);
+ for(i=0; (i<ngid); i++)
+ {
+ snew(ns->ns_buf[i],SHIFTS);
+ }
+ ncg = ncg_mtop(mtop);
+ snew(ns->simple_aaj,2*ncg);
+ for(jcg=0; (jcg<ncg); jcg++)
+ {
+ ns->simple_aaj[jcg] = jcg;
+ ns->simple_aaj[jcg+ncg] = jcg;
+ }
+ }
+
+ /* Create array that determines whether or not atoms have VdW */
+ snew(ns->bHaveVdW,fr->ntype);
+ for(i=0; (i<fr->ntype); i++)
+ {
+ for(j=0; (j<fr->ntype); j++)
+ {
+ ns->bHaveVdW[i] = (ns->bHaveVdW[i] ||
+ (fr->bBHAM ?
+ ((BHAMA(fr->nbfp,fr->ntype,i,j) != 0) ||
+ (BHAMB(fr->nbfp,fr->ntype,i,j) != 0) ||
+ (BHAMC(fr->nbfp,fr->ntype,i,j) != 0)) :
+ ((C6(fr->nbfp,fr->ntype,i,j) != 0) ||
+ (C12(fr->nbfp,fr->ntype,i,j) != 0))));
+ }
+ }
+ if (debug)
+ pr_bvec(debug,0,"bHaveVdW",ns->bHaveVdW,fr->ntype,TRUE);
+
+ ns->nra_alloc = 0;
+ ns->bexcl = NULL;
+ if (!DOMAINDECOMP(cr))
+ {
+ /* This could be reduced with particle decomposition */
+ ns_realloc_natoms(ns,mtop->natoms);
+ }
+
+ ns->nblist_initialized=FALSE;
+
+ /* nbr list debug dump */
+ {
+ char *ptr=getenv("GMX_DUMP_NL");
+ if (ptr)
+ {
+ ns->dump_nl=strtol(ptr,NULL,10);
+ if (fplog)
+ {
+ fprintf(fplog, "GMX_DUMP_NL = %d", ns->dump_nl);
+ }
+ }
+ else
+ {
+ ns->dump_nl=0;
+ }
+ }
+}
+
+
+int search_neighbours(FILE *log,t_forcerec *fr,
+ rvec x[],matrix box,
+ gmx_localtop_t *top,
+ gmx_groups_t *groups,
+ t_commrec *cr,
+ t_nrnb *nrnb,t_mdatoms *md,
+ real lambda,real *dvdlambda,
+ gmx_grppairener_t *grppener,
+ gmx_bool bFillGrid,
+ gmx_bool bDoLongRange,
+ gmx_bool bDoForces,rvec *f)
+{
+ t_block *cgs=&(top->cgs);
+ rvec box_size,grid_x0,grid_x1;
+ int i,j,m,ngid;
+ real min_size,grid_dens;
+ int nsearch;
+ gmx_bool bGrid;
+ char *ptr;
+ gmx_bool *i_egp_flags;
+ int cg_start,cg_end,start,end;
+ gmx_ns_t *ns;
+ t_grid *grid;
+ gmx_domdec_zones_t *dd_zones;
+ put_in_list_t *put_in_list;
+
+ ns = &fr->ns;
+
+ /* Set some local variables */
+ bGrid = fr->bGrid;
+ ngid = groups->grps[egcENER].nr;
+
+ for(m=0; (m<DIM); m++)
+ {
+ box_size[m] = box[m][m];
+ }
+
+ if (fr->ePBC != epbcNONE)
+ {
+ if (sqr(fr->rlistlong) >= max_cutoff2(fr->ePBC,box))
+ {
+ gmx_fatal(FARGS,"One of the box vectors has become shorter than twice the cut-off length or box_yy-|box_zy| or box_zz has become smaller than the cut-off.");
+ }
+ if (!bGrid)
+ {
+ min_size = min(box_size[XX],min(box_size[YY],box_size[ZZ]));
+ if (2*fr->rlistlong >= min_size)
+ gmx_fatal(FARGS,"One of the box diagonal elements has become smaller than twice the cut-off length.");
+ }
+ }
+
+ if (DOMAINDECOMP(cr))
+ {
+ ns_realloc_natoms(ns,cgs->index[cgs->nr]);
+ }
+ debug_gmx();
+
+ /* Reset the neighbourlists */
+ reset_neighbor_list(fr,FALSE,-1,-1);
+
+ if (bGrid && bFillGrid)
+ {
+
+ grid = ns->grid;
+ if (DOMAINDECOMP(cr))
+ {
+ dd_zones = domdec_zones(cr->dd);
+ }
+ else
+ {
+ dd_zones = NULL;
+
+ get_nsgrid_boundaries(grid,NULL,box,NULL,NULL,NULL,
+ cgs->nr,fr->cg_cm,grid_x0,grid_x1,&grid_dens);
+
+ grid_first(log,grid,NULL,NULL,fr->ePBC,box,grid_x0,grid_x1,
+ fr->rlistlong,grid_dens);
+ }
+ debug_gmx();
+
+ /* Don't know why this all is... (DvdS 3/99) */
+#ifndef SEGV
+ start = 0;
+ end = cgs->nr;
+#else
+ start = fr->cg0;
+ end = (cgs->nr+1)/2;
+#endif
+
+ if (DOMAINDECOMP(cr))
+ {
+ end = cgs->nr;
+ fill_grid(log,dd_zones,grid,end,-1,end,fr->cg_cm);
+ grid->icg0 = 0;
+ grid->icg1 = dd_zones->izone[dd_zones->nizone-1].cg1;
+ }
+ else
+ {
+ fill_grid(log,NULL,grid,cgs->nr,fr->cg0,fr->hcg,fr->cg_cm);
+ grid->icg0 = fr->cg0;
+ grid->icg1 = fr->hcg;
+ debug_gmx();
+
+ if (PARTDECOMP(cr))
+ mv_grid(cr,grid);
+ debug_gmx();
+ }
+
+ calc_elemnr(log,grid,start,end,cgs->nr);
+ calc_ptrs(grid);
+ grid_last(log,grid,start,end,cgs->nr);
+
+ if (gmx_debug_at)
+ {
+ check_grid(debug,grid);
+ print_grid(debug,grid);
+ }
+ }
+ else if (fr->n_tpi)
+ {
+ /* Set the grid cell index for the test particle only.
+ * The cell to cg index is not corrected, but that does not matter.
+ */
+ fill_grid(log,NULL,ns->grid,fr->hcg,fr->hcg-1,fr->hcg,fr->cg_cm);
+ }
+ debug_gmx();
+
+ if (!fr->ns.bCGlist)
+ {
+ put_in_list = put_in_list_at;
+ }
+ else
+ {
+ put_in_list = put_in_list_cg;
+ }
+
+ /* Do the core! */
+ if (bGrid)
+ {
+ grid = ns->grid;
+ nsearch = nsgrid_core(log,cr,fr,box,box_size,ngid,top,
+ grid,x,ns->bexcl,ns->bExcludeAlleg,
+ nrnb,md,lambda,dvdlambda,grppener,
+ put_in_list,ns->bHaveVdW,
+ bDoLongRange,bDoForces,f,
+ FALSE);
+
+ /* neighbour searching withouth QMMM! QM atoms have zero charge in
+ * the classical calculation. The charge-charge interaction
+ * between QM and MM atoms is handled in the QMMM core calculation
+ * (see QMMM.c). The VDW however, we'd like to compute classically
+ * and the QM MM atom pairs have just been put in the
+ * corresponding neighbourlists. in case of QMMM we still need to
+ * fill a special QMMM neighbourlist that contains all neighbours
+ * of the QM atoms. If bQMMM is true, this list will now be made:
+ */
+ if (fr->bQMMM && fr->qr->QMMMscheme!=eQMMMschemeoniom)
+ {
+ nsearch += nsgrid_core(log,cr,fr,box,box_size,ngid,top,
+ grid,x,ns->bexcl,ns->bExcludeAlleg,
+ nrnb,md,lambda,dvdlambda,grppener,
+ put_in_list_qmmm,ns->bHaveVdW,
+ bDoLongRange,bDoForces,f,
+ TRUE);
+ }
+ }
+ else
+ {
+ nsearch = ns_simple_core(fr,top,md,box,box_size,
+ ns->bexcl,ns->simple_aaj,
+ ngid,ns->ns_buf,put_in_list,ns->bHaveVdW);
+ }
+ debug_gmx();
+
+#ifdef DEBUG
+ pr_nsblock(log);
+#endif
+
+ inc_nrnb(nrnb,eNR_NS,nsearch);
+ /* inc_nrnb(nrnb,eNR_LR,fr->nlr); */
+
+ return nsearch;
+}
+
+int natoms_beyond_ns_buffer(t_inputrec *ir,t_forcerec *fr,t_block *cgs,
+ matrix scale_tot,rvec *x)
+{
+ int cg0,cg1,cg,a0,a1,a,i,j;
+ real rint,hbuf2,scale;
+ rvec *cg_cm,cgsc;
+ gmx_bool bIsotropic;
+ int nBeyond;
+
+ nBeyond = 0;
+
+ rint = max(ir->rcoulomb,ir->rvdw);
+ if (ir->rlist < rint)
+ {
+ gmx_fatal(FARGS,"The neighbor search buffer has negative size: %f nm",
+ ir->rlist - rint);
+ }
+ cg_cm = fr->cg_cm;
+
+ cg0 = fr->cg0;
+ cg1 = fr->hcg;
+
+ if (!EI_DYNAMICS(ir->eI) || !DYNAMIC_BOX(*ir))
+ {
+ hbuf2 = sqr(0.5*(ir->rlist - rint));
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ a0 = cgs->index[cg];
+ a1 = cgs->index[cg+1];
+ for(a=a0; a<a1; a++)
+ {
+ if (distance2(cg_cm[cg],x[a]) > hbuf2)
+ {
+ nBeyond++;
+ }
+ }
+ }
+ }
+ else
+ {
+ bIsotropic = TRUE;
+ scale = scale_tot[0][0];
+ for(i=1; i<DIM; i++)
+ {
+ /* With anisotropic scaling, the original spherical ns volumes become
+ * ellipsoids. To avoid costly transformations we use the minimum
+ * eigenvalue of the scaling matrix for determining the buffer size.
+ * Since the lower half is 0, the eigenvalues are the diagonal elements.
+ */
+ scale = min(scale,scale_tot[i][i]);
+ if (scale_tot[i][i] != scale_tot[i-1][i-1])
+ {
+ bIsotropic = FALSE;
+ }
+ for(j=0; j<i; j++)
+ {
+ if (scale_tot[i][j] != 0)
+ {
+ bIsotropic = FALSE;
+ }
+ }
+ }
+ hbuf2 = sqr(0.5*(scale*ir->rlist - rint));
+ if (bIsotropic)
+ {
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ svmul(scale,cg_cm[cg],cgsc);
+ a0 = cgs->index[cg];
+ a1 = cgs->index[cg+1];
+ for(a=a0; a<a1; a++)
+ {
+ if (distance2(cgsc,x[a]) > hbuf2)
+ {
+ nBeyond++;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Anistropic scaling */
+ for(cg=cg0; cg<cg1; cg++)
+ {
+ /* Since scale_tot contains the transpose of the scaling matrix,
+ * we need to multiply with the transpose.
+ */
+ tmvmul_ur0(scale_tot,cg_cm[cg],cgsc);
+ a0 = cgs->index[cg];
+ a1 = cgs->index[cg+1];
+ for(a=a0; a<a1; a++)
+ {
+ if (distance2(cgsc,x[a]) > hbuf2)
+ {
+ nBeyond++;
+ }
+ }
+ }
+ }
+ }
+
+ return nBeyond;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "domdec.h"
+#include "gmx_wallcycle.h"
+#include "trnio.h"
+#include "smalloc.h"
+#include "network.h"
+#include "pbc.h"
+#include "futil.h"
+#include "mdrun.h"
+#include "txtdump.h"
+#include "names.h"
+#include "mtop_util.h"
+#include "names.h"
+#include "nrjac.h"
+#include "vec.h"
+#include "gmx_ga2la.h"
+#include "xvgr.h"
+#include "gmxfio.h"
+#include "mpelogging.h"
+#include "groupcoord.h"
+#include "pull_rotation.h"
+#include "gmx_sort.h"
+
+
+static char *RotStr = {"Enforced rotation:"};
+
+
+/* Set the minimum weight for the determination of the slab centers */
+#define WEIGHT_MIN (10*GMX_FLOAT_MIN)
+
+/* Helper structure for sorting positions along rotation vector */
+typedef struct {
+ real xcproj; /* Projection of xc on the rotation vector */
+ int ind; /* Index of xc */
+ real m; /* Mass */
+ rvec x; /* Position */
+ rvec x_ref; /* Reference position */
+} sort_along_vec_t;
+
+
+/* Enforced rotation / flexible: determine the angle of each slab */
+typedef struct gmx_slabdata
+{
+ int nat; /* Number of atoms belonging to this slab */
+ rvec *x; /* The positions belonging to this slab. In
+ general, this should be all positions of the
+ whole rotation group, but we leave those away
+ that have a small enough weight */
+ rvec *ref; /* Same for reference */
+ real *weight; /* The weight for each atom */
+} t_gmx_slabdata;
+
+
+/* Enforced rotation data for all groups */
+typedef struct gmx_enfrot
+{
+ FILE *out_rot; /* Output file for rotation data */
+ FILE *out_torque; /* Output file for torque data */
+ FILE *out_angles; /* Output file for slab angles for flexible type */
+ FILE *out_slabs; /* Output file for slab centers */
+ int bufsize; /* Allocation size of buf */
+ rvec *xbuf; /* Coordinate buffer variable for sorting */
+ real *mbuf; /* Masses buffer variable for sorting */
+ sort_along_vec_t *data; /* Buffer variable needed for position sorting */
+ real *mpi_inbuf; /* MPI buffer */
+ real *mpi_outbuf; /* MPI buffer */
+ int mpi_bufsize; /* Allocation size of in & outbuf */
+ real Vrot; /* (Local) part of the enf. rotation potential */
+ unsigned long Flags; /* mdrun flags */
+ gmx_bool bOut; /* Used to skip first output when appending to
+ * avoid duplicate entries in rotation outfiles */
+} t_gmx_enfrot;
+
+
+/* Global enforced rotation data for a single rotation group */
+typedef struct gmx_enfrotgrp
+{
+ real degangle; /* Rotation angle in degrees */
+ matrix rotmat; /* Rotation matrix */
+ atom_id *ind_loc; /* Local rotation indices */
+ int nat_loc; /* Number of local group atoms */
+ int nalloc_loc; /* Allocation size for ind_loc and weight_loc */
+
+ real V; /* Rotation potential for this rotation group */
+ rvec *f_rot_loc; /* Array to store the forces on the local atoms
+ resulting from enforced rotation potential */
+
+ /* Collective coordinates for the whole rotation group */
+ real *xc_ref_length; /* Length of each x_rotref vector after x_rotref
+ has been put into origin */
+ int *xc_ref_ind; /* Position of each local atom in the collective
+ array */
+ rvec xc_center; /* Center of the rotation group positions, may
+ be mass weighted */
+ rvec xc_ref_center; /* dito, for the reference positions */
+ rvec *xc; /* Current (collective) positions */
+ ivec *xc_shifts; /* Current (collective) shifts */
+ ivec *xc_eshifts; /* Extra shifts since last DD step */
+ rvec *xc_old; /* Old (collective) positions */
+ rvec *xc_norm; /* Normalized form of the current positions */
+ rvec *xc_ref_sorted; /* Reference positions (sorted in the same order
+ as xc when sorted) */
+ int *xc_sortind; /* Where is a position found after sorting? */
+ real *mc; /* Collective masses */
+ real *mc_sorted;
+ real invmass; /* one over the total mass of the rotation group */
+
+ real torque_v; /* Torque in the direction of rotation vector */
+ real angle_v; /* Actual angle of the whole rotation group */
+ /* Fixed rotation only */
+ real weight_v; /* Weights for angle determination */
+ rvec *xr_loc; /* Local reference coords, correctly rotated */
+ rvec *x_loc_pbc; /* Local current coords, correct PBC image */
+ real *m_loc; /* Masses of the current local atoms */
+
+ /* Flexible rotation only */
+ int nslabs_alloc; /* For this many slabs memory is allocated */
+ int slab_first; /* Lowermost slab for that the calculation needs
+ to be performed at a given time step */
+ int slab_last; /* Uppermost slab ... */
+ int slab_first_ref; /* First slab for which ref. center is stored */
+ int slab_last_ref; /* Last ... */
+ int slab_buffer; /* Slab buffer region around reference slabs */
+ int *firstatom; /* First relevant atom for a slab */
+ int *lastatom; /* Last relevant atom for a slab */
+ rvec *slab_center; /* Gaussian-weighted slab center */
+ rvec *slab_center_ref; /* Gaussian-weighted slab center for the
+ reference positions */
+ real *slab_weights; /* Sum of gaussian weights in a slab */
+ real *slab_torque_v; /* Torque T = r x f for each slab. */
+ /* torque_v = m.v = angular momentum in the
+ direction of v */
+ real max_beta; /* min_gaussian from inputrec->rotgrp is the
+ minimum value the gaussian must have so that
+ the force is actually evaluated max_beta is
+ just another way to put it */
+ real *gn_atom; /* Precalculated gaussians for a single atom */
+ int *gn_slabind; /* Tells to which slab each precalculated gaussian
+ belongs */
+ rvec *slab_innersumvec;/* Inner sum of the flexible2 potential per slab;
+ this is precalculated for optimization reasons */
+ t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights
+ of atoms belonging to a slab */
+} t_gmx_enfrotgrp;
+
+
+/* Activate output of forces for correctness checks */
+/* #define PRINT_FORCES */
+#ifdef PRINT_FORCES
+#define PRINT_FORCE_J fprintf(stderr,"f%d = %15.8f %15.8f %15.8f\n",erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
+#define PRINT_POT_TAU if (MASTER(cr)) { \
+ fprintf(stderr,"potential = %15.8f\n" "torque = %15.8f\n", erg->V, erg->torque_v); \
+ }
+#else
+#define PRINT_FORCE_J
+#define PRINT_POT_TAU
+#endif
+
+/* Shortcuts for often used queries */
+#define ISFLEX(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) )
+#define ISCOLL(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) || (rg->eType==erotgRMPF) || (rg->eType==erotgRM2PF) )
+
+
+/* Does any of the rotation groups use slab decomposition? */
+static gmx_bool HaveFlexibleGroups(t_rot *rot)
+{
+ int g;
+ t_rotgrp *rotg;
+ gmx_bool bHaveFlexGroups=FALSE;
+
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ bHaveFlexGroups = TRUE;
+ }
+
+ return bHaveFlexGroups;
+}
+
+
+static double** allocate_square_matrix(int dim)
+{
+ int i;
+ double** mat = NULL;
+
+
+ snew(mat, dim);
+ for(i=0; i<dim; i++)
+ snew(mat[i], dim);
+
+ return mat;
+}
+
+
+static void free_square_matrix(double** mat, int dim)
+{
+ int i;
+
+
+ for (i=0; i<dim; i++)
+ sfree(mat[i]);
+ sfree(mat);
+}
+
+
+/* Output rotation energy, torques, etc. for each rotation group */
+static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
+{
+ int g,i,islab,nslabs=0;
+ int count; /* MPI element counter */
+ t_rotgrp *rotg;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ gmx_bool bFlex;
+
+
+ er=rot->enfrot;
+
+ /* Fill the MPI buffer with stuff to reduce: */
+ if (PAR(cr))
+ {
+ count=0;
+ for (g=0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ er->mpi_inbuf[count++] = erg->V;
+ er->mpi_inbuf[count++] = erg->torque_v;
+ er->mpi_inbuf[count++] = erg->angle_v;
+ er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
+ if (ISFLEX(rotg))
+ {
+ /* (Re-)allocate memory for MPI buffer: */
+ if (er->mpi_bufsize < count+nslabs)
+ {
+ er->mpi_bufsize = count+nslabs;
+ srenew(er->mpi_inbuf , er->mpi_bufsize);
+ srenew(er->mpi_outbuf, er->mpi_bufsize);
+ }
+ for (i=0; i<nslabs; i++)
+ er->mpi_inbuf[count++] = erg->slab_torque_v[i];
+ }
+ }
+#ifdef GMX_MPI
+ MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
+#endif
+ /* Copy back the reduced data from the buffer on the master */
+ if (MASTER(cr))
+ {
+ count=0;
+ for (g=0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ erg->V = er->mpi_outbuf[count++];
+ erg->torque_v = er->mpi_outbuf[count++];
+ erg->angle_v = er->mpi_outbuf[count++];
+ erg->weight_v = er->mpi_outbuf[count++];
+ if (ISFLEX(rotg))
+ {
+ for (i=0; i<nslabs; i++)
+ erg->slab_torque_v[i] = er->mpi_outbuf[count++];
+ }
+ }
+ }
+ }
+
+ /* Output */
+ if (MASTER(cr))
+ {
+ /* Angle and torque for each rotation group */
+ for (g=0; g < rot->ngrp; g++)
+ {
+ rotg=&rot->grp[g];
+ bFlex = ISFLEX(rotg);
+
+ erg=rotg->enfrotgrp;
+
+ /* Output to main rotation output file: */
+ if ( do_per_step(step, rot->nstrout) )
+ {
+ if (bFlex)
+ fprintf(er->out_rot, "%12.4f", erg->angle_v); /* RMSD fit angle */
+ else
+ fprintf(er->out_rot, "%12.4f", (erg->angle_v/erg->weight_v)*180.0*M_1_PI);
+ fprintf(er->out_rot, "%12.3e", erg->torque_v);
+ fprintf(er->out_rot, "%12.3e", erg->V);
+ }
+
+ /* Output to torque log file: */
+ if ( bFlex && do_per_step(step, rot->nstsout) )
+ {
+ fprintf(er->out_torque, "%12.3e%6d", t, g);
+ for (i=erg->slab_first; i<=erg->slab_last; i++)
+ {
+ islab = i - erg->slab_first; /* slab index */
+ /* Only output if enough weight is in slab */
+ if (erg->slab_weights[islab] > rotg->min_gaussian)
+ fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
+ }
+ fprintf(er->out_torque , "\n");
+ }
+ }
+ if ( do_per_step(step, rot->nstrout) )
+ fprintf(er->out_rot, "\n");
+ }
+}
+
+
+/* Add the forces from enforced rotation potential to the local forces.
+ * Should be called after the SR forces have been evaluated */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
+{
+ int g,l,ii;
+ t_rotgrp *rotg;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ er=rot->enfrot;
+
+ GMX_MPE_LOG(ev_add_rot_forces_start);
+
+ /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
+ * on the master and output these values to file. */
+ if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
+ reduce_output(cr, rot, t, step);
+
+ /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
+ er->bOut = TRUE;
+
+ /* Total rotation potential is the sum over all rotation groups */
+ er->Vrot = 0.0;
+
+ /* Loop over enforced rotation groups (usually 1, though)
+ * Apply the forces from rotation potentials */
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+ er->Vrot += erg->V;
+ for (l=0; l<erg->nat_loc; l++)
+ {
+ /* Get the right index of the local force */
+ ii = erg->ind_loc[l];
+ /* Add */
+ rvec_inc(f[ii],erg->f_rot_loc[l]);
+ }
+ }
+
+ PRINT_POT_TAU
+
+ GMX_MPE_LOG(ev_add_rot_forces_finish);
+
+ return (MASTER(cr)? er->Vrot : 0.0);
+}
+
+
+/* The Gaussian norm is chosen such that the sum of the gaussian functions
+ * over the slabs is approximately 1.0 everywhere */
+#define GAUSS_NORM 0.569917543430618
+
+
+/* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
+ * also does some checks
+ */
+static double calc_beta_max(real min_gaussian, real slab_dist)
+{
+ double sigma;
+ double arg;
+
+
+ /* Actually the next two checks are already made in grompp */
+ if (slab_dist <= 0)
+ gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
+ if (min_gaussian <= 0)
+ gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
+
+ /* Define the sigma value */
+ sigma = 0.7*slab_dist;
+
+ /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
+ arg = min_gaussian/GAUSS_NORM;
+ if (arg > 1.0)
+ gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
+
+ return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
+}
+
+
+static inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
+{
+ return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
+}
+
+
+static inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
+{
+ const real norm = GAUSS_NORM;
+ real sigma;
+
+
+ /* Define the sigma value */
+ sigma = 0.7*rotg->slab_dist;
+ /* Calculate the Gaussian value of slab n for position curr_x */
+ return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
+}
+
+
+/* Returns the weight in a single slab, also calculates the Gaussian- and mass-
+ * weighted sum of positions for that slab */
+static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
+{
+ rvec curr_x; /* The position of an atom */
+ rvec curr_x_weighted; /* The gaussian-weighted position */
+ real gaussian; /* A single gaussian weight */
+ real wgauss; /* gaussian times current mass */
+ real slabweight = 0.0; /* The sum of weights in the slab */
+ int i,islab;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+ clear_rvec(*x_weighted_sum);
+
+ /* Slab index */
+ islab = j - erg->slab_first;
+
+ /* Loop over all atoms in the rotation group */
+ for (i=0; i<rotg->nat; i++)
+ {
+ copy_rvec(xc[i], curr_x);
+ gaussian = gaussian_weight(curr_x, rotg, j);
+ wgauss = gaussian * mc[i];
+ svmul(wgauss, curr_x, curr_x_weighted);
+ rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
+ slabweight += wgauss;
+ } /* END of loop over rotation group atoms */
+
+ return slabweight;
+}
+
+
+static void get_slab_centers(
+ t_rotgrp *rotg, /* The rotation group information */
+ rvec *xc, /* The rotation group positions; will
+ typically be enfrotgrp->xc, but at first call
+ it is enfrotgrp->xc_ref */
+ real *mc, /* The masses of the rotation group atoms */
+ t_commrec *cr, /* Communication record */
+ int g, /* The number of the rotation group */
+ real time, /* Used for output only */
+ FILE *out_slabs, /* For outputting center per slab information */
+ gmx_bool bOutStep, /* Is this an output step? */
+ gmx_bool bReference) /* If this routine is called from
+ init_rot_group we need to store
+ the reference slab centers */
+{
+ int j,islab;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Loop over slabs */
+ for (j = erg->slab_first; j <= erg->slab_last; j++)
+ {
+ islab = j - erg->slab_first;
+ erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
+
+ /* We can do the calculations ONLY if there is weight in the slab! */
+ if (erg->slab_weights[islab] > WEIGHT_MIN)
+ {
+ svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
+ }
+ else
+ {
+ /* We need to check this here, since we divide through slab_weights
+ * in the flexible low-level routines! */
+ gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
+ }
+
+ /* At first time step: save the centers of the reference structure */
+ if (bReference)
+ copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
+ } /* END of loop over slabs */
+
+ /* Output on the master */
+ if (MASTER(cr) && bOutStep)
+ {
+ fprintf(out_slabs, "%12.3e%6d", time, g);
+ for (j = erg->slab_first; j <= erg->slab_last; j++)
+ {
+ islab = j - erg->slab_first;
+ fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
+ j,erg->slab_center[islab][XX],erg->slab_center[islab][YY],erg->slab_center[islab][ZZ]);
+ }
+ fprintf(out_slabs, "\n");
+ }
+}
+
+
+static void calc_rotmat(
+ rvec vec,
+ real degangle, /* Angle alpha of rotation at time t in degrees */
+ matrix rotmat) /* Rotation matrix */
+{
+ real radangle; /* Rotation angle in radians */
+ real cosa; /* cosine alpha */
+ real sina; /* sine alpha */
+ real OMcosa; /* 1 - cos(alpha) */
+ real dumxy, dumxz, dumyz; /* save computations */
+ rvec rot_vec; /* Rotate around rot_vec ... */
+
+
+ radangle = degangle * M_PI/180.0;
+ copy_rvec(vec , rot_vec );
+
+ /* Precompute some variables: */
+ cosa = cos(radangle);
+ sina = sin(radangle);
+ OMcosa = 1.0 - cosa;
+ dumxy = rot_vec[XX]*rot_vec[YY]*OMcosa;
+ dumxz = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
+ dumyz = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
+
+ /* Construct the rotation matrix for this rotation group: */
+ /* 1st column: */
+ rotmat[XX][XX] = cosa + rot_vec[XX]*rot_vec[XX]*OMcosa;
+ rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
+ rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
+ /* 2nd column: */
+ rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
+ rotmat[YY][YY] = cosa + rot_vec[YY]*rot_vec[YY]*OMcosa;
+ rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
+ /* 3rd column: */
+ rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
+ rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
+ rotmat[ZZ][ZZ] = cosa + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
+
+#ifdef PRINTMATRIX
+ int iii,jjj;
+
+ for (iii=0; iii<3; iii++) {
+ for (jjj=0; jjj<3; jjj++)
+ fprintf(stderr, " %10.8f ", rotmat[iii][jjj]);
+ fprintf(stderr, "\n");
+ }
+#endif
+}
+
+
+/* Calculates torque on the rotation axis tau = position x force */
+static inline real torque(
+ rvec rotvec, /* rotation vector; MUST be normalized! */
+ rvec force, /* force */
+ rvec x, /* position of atom on which the force acts */
+ rvec pivot) /* pivot point of rotation axis */
+{
+ rvec vectmp, tau;
+
+
+ /* Subtract offset */
+ rvec_sub(x,pivot,vectmp);
+
+ /* position x force */
+ cprod(vectmp, force, tau);
+
+ /* Return the part of the torque which is parallel to the rotation vector */
+ return iprod(tau, rotvec);
+}
+
+
+/* Right-aligned output of value with standard width */
+static void print_aligned(FILE *fp, char *str)
+{
+ fprintf(fp, "%12s", str);
+}
+
+
+/* Right-aligned output of value with standard short width */
+static void print_aligned_short(FILE *fp, char *str)
+{
+ fprintf(fp, "%6s", str);
+}
+
+
+static FILE *open_output_file(const char *fn, int steps, const char what[])
+{
+ FILE *fp;
+
+
+ fp = ffopen(fn, "w");
+
+ fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
+ what,steps, steps>1 ? "s":"");
+
+ return fp;
+}
+
+
+/* Open output file for slab center data. Call on master only */
+static FILE *open_slab_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g,i;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
+ g, erotg_names[rotg->eType], rotg->slab_dist,
+ rotg->bMassW? "centers of mass":"geometrical centers");
+ }
+ }
+
+ fprintf(fp, "# Reference centers are listed first (t=-1).\n");
+ fprintf(fp, "# The following columns have the syntax:\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ /* Print legend for the first two entries only ... */
+ for (i=0; i<2; i++)
+ {
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "X center");
+ print_aligned(fp, "Y center");
+ print_aligned(fp, "Z center");
+ }
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Open output file and print some general information about the rotation groups.
+ * Call on master only */
+static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g,nsets;
+ t_rotgrp *rotg;
+ const char **setname;
+ char buf[50], buf2[75];
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ gmx_bool bFlex;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ fp = xvgropen(fn, "Rotation angles and energy", "Time [ps]", "angles [degrees] and energies [kJ/mol]", oenv);
+ fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s":"");
+ fprintf(fp, "# The scalar tau is the torque [kJ/mol] in the direction of the rotation vector v.\n");
+ fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
+ fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
+ fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
+ fprintf(fp, "#\n");
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+ bFlex = ISFLEX(rotg);
+
+ fprintf(fp, "# Rotation group %d, potential type '%s':\n" , g, erotg_names[rotg->eType]);
+ fprintf(fp, "# rot_massw%d %s\n" , g, yesno_names[rotg->bMassW]);
+ fprintf(fp, "# rot_vec%d %12.5e %12.5e %12.5e\n" , g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+ fprintf(fp, "# rot_rate%d %12.5e degrees/ps\n" , g, rotg->rate);
+ fprintf(fp, "# rot_k%d %12.5e kJ/(mol*nm^2)\n" , g, rotg->k);
+ if ( rotg->eType==erotgISO || rotg->eType==erotgPM || rotg->eType==erotgRM || rotg->eType==erotgRM2)
+ fprintf(fp, "# rot_pivot%d %12.5e %12.5e %12.5e nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
+
+ if (bFlex)
+ {
+ fprintf(fp, "# rot_slab_distance%d %f nm\n", g, rotg->slab_dist);
+ fprintf(fp, "# rot_min_gaussian%d %12.5e\n", g, rotg->min_gaussian);
+ }
+
+ /* Output the centers of the rotation groups for the pivot-free potentials */
+ if ((rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF
+ || (rotg->eType==erotgFLEXT) || (rotg->eType==erotgFLEX2T)) )
+ {
+ fprintf(fp, "# ref. grp. %d center %12.5e %12.5e %12.5e\n", g,
+ erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
+
+ fprintf(fp, "# grp. %d init.center %12.5e %12.5e %12.5e\n", g,
+ erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
+ }
+
+ if ( (rotg->eType == erotgRM2) || (rotg->eType==erotgFLEX2) || (rotg->eType==erotgFLEX2T) )
+ {
+ fprintf(fp, "# rot_eps%d %12.5e nm^2\n", g, rotg->eps);
+ }
+ }
+
+ fprintf(fp, "#\n# Legend for the following data columns:\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ nsets = 0;
+ snew(setname, 4*rot->ngrp);
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ sprintf(buf, "theta_ref%d", g);
+ print_aligned(fp, buf);
+ sprintf(buf2, "%s [degrees]", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+ }
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ bFlex = ISFLEX(rotg);
+
+ /* For flexible axis rotation we use RMSD fitting to determine the
+ * actual angle of the rotation group */
+ if (bFlex)
+ sprintf(buf, "theta-fit%d", g);
+ else
+ sprintf(buf, "theta-av%d", g);
+ print_aligned(fp, buf);
+ sprintf(buf2, "%s [degrees]", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+
+ sprintf(buf, "tau%d", g);
+ print_aligned(fp, buf);
+ sprintf(buf2, "%s [kJ/mol]", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+
+ sprintf(buf, "energy%d", g);
+ print_aligned(fp, buf);
+ sprintf(buf2, "%s [kJ/mol]", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+ }
+ fprintf(fp, "\n#\n");
+
+ if (nsets > 1)
+ xvgr_legend(fp, nsets, setname, oenv);
+ sfree(setname);
+
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Call on master only */
+static FILE *open_angles_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ int g;
+ FILE *fp;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ /* Open output file and write some information about it's structure: */
+ fp = open_output_file(fn, rot->nstsout, "rotation group angles");
+ fprintf(fp, "# All angles given in degrees, time in ps.\n");
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, fit type %s.\n",
+ g, erotg_names[rotg->eType], rotg->slab_dist, erotg_fitnames[rotg->eFittype]);
+ }
+ }
+ fprintf(fp, "# Legend for the following data columns:\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ print_aligned(fp, "theta_ref");
+ print_aligned_short(fp, "slab");
+ print_aligned_short(fp, "atoms");
+ print_aligned(fp, "theta_fit");
+ print_aligned_short(fp, "slab");
+ print_aligned_short(fp, "atoms");
+ print_aligned(fp, "theta_fit");
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Open torque output file and write some information about it's structure.
+ * Call on master only */
+static FILE *open_torque_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ fp = open_output_file(fn, rot->nstsout,"torques");
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
+ fprintf(fp, "# The scalar tau is the torque [kJ/mol] in the direction of the rotation vector.\n");
+ fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
+ fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+ fprintf(fp, "#\n");
+ }
+ }
+ fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "tau");
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "tau");
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+static void swap_val(double* vec, int i, int j)
+{
+ double tmp = vec[j];
+
+
+ vec[j]=vec[i];
+ vec[i]=tmp;
+}
+
+
+static void swap_col(double **mat, int i, int j)
+{
+ double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
+
+
+ mat[0][j]=mat[0][i];
+ mat[1][j]=mat[1][i];
+ mat[2][j]=mat[2][i];
+
+ mat[0][i]=tmp[0];
+ mat[1][i]=tmp[1];
+ mat[2][i]=tmp[2];
+}
+
+
+/* Eigenvectors are stored in columns of eigen_vec */
+static void diagonalize_symmetric(
+ double **matrix,
+ double **eigen_vec,
+ double eigenval[3])
+{
+ int n_rot;
+
+
+ jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
+
+ /* sort in ascending order */
+ if (eigenval[0] > eigenval[1])
+ {
+ swap_val(eigenval, 0, 1);
+ swap_col(eigen_vec, 0, 1);
+ }
+ if (eigenval[1] > eigenval[2])
+ {
+ swap_val(eigenval, 1, 2);
+ swap_col(eigen_vec, 1, 2);
+ }
+ if (eigenval[0] > eigenval[1])
+ {
+ swap_val(eigenval, 0, 1);
+ swap_col(eigen_vec, 0, 1);
+ }
+}
+
+
+static void align_with_z(
+ rvec* s, /* Structure to align */
+ int natoms,
+ rvec axis)
+{
+ int i, j, k;
+ rvec zet = {0.0, 0.0, 1.0};
+ rvec rot_axis={0.0, 0.0, 0.0};
+ rvec *rotated_str=NULL;
+ real ooanorm;
+ real angle;
+ matrix rotmat;
+
+
+ snew(rotated_str, natoms);
+
+ /* Normalize the axis */
+ ooanorm = 1.0/norm(axis);
+ svmul(ooanorm, axis, axis);
+
+ /* Calculate the angle for the fitting procedure */
+ cprod(axis, zet, rot_axis);
+ angle = acos(axis[2]);
+ if (angle < 0.0)
+ angle += M_PI;
+
+ /* Calculate the rotation matrix */
+ calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
+
+ /* Apply the rotation matrix to s */
+ for (i=0; i<natoms; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ for(k=0; k<3; k++)
+ {
+ rotated_str[i][j] += rotmat[j][k]*s[i][k];
+ }
+ }
+ }
+
+ /* Rewrite the rotated structure to s */
+ for(i=0; i<natoms; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ s[i][j]=rotated_str[i][j];
+ }
+ }
+
+ sfree(rotated_str);
+}
+
+
+static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
+{
+ int i, j, k;
+
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ Rmat[i][j] = 0.0;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ for (k=0; k<natoms; k++)
+ Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
+}
+
+
+static void weigh_coords(rvec* str, real* weight, int natoms)
+{
+ int i, j;
+
+
+ for(i=0; i<natoms; i++)
+ {
+ for(j=0; j<3; j++)
+ str[i][j] *= sqrt(weight[i]);
+ }
+}
+
+
+static real opt_angle_analytic(
+ rvec* ref_s,
+ rvec* act_s,
+ real* weight,
+ int natoms,
+ rvec ref_com,
+ rvec act_com,
+ rvec axis)
+{
+ int i, j, k;
+ rvec *ref_s_1=NULL;
+ rvec *act_s_1=NULL;
+ rvec shift;
+ double **Rmat, **RtR, **eigvec;
+ double eigval[3];
+ double V[3][3], WS[3][3];
+ double rot_matrix[3][3];
+ double opt_angle;
+
+
+ /* Do not change the original coordinates */
+ snew(ref_s_1, natoms);
+ snew(act_s_1, natoms);
+ for(i=0; i<natoms; i++)
+ {
+ copy_rvec(ref_s[i], ref_s_1[i]);
+ copy_rvec(act_s[i], act_s_1[i]);
+ }
+
+ /* Translate the structures to the origin */
+ shift[XX] = -ref_com[XX];
+ shift[YY] = -ref_com[YY];
+ shift[ZZ] = -ref_com[ZZ];
+ translate_x(ref_s_1, natoms, shift);
+
+ shift[XX] = -act_com[XX];
+ shift[YY] = -act_com[YY];
+ shift[ZZ] = -act_com[ZZ];
+ translate_x(act_s_1, natoms, shift);
+
+ /* Align rotation axis with z */
+ align_with_z(ref_s_1, natoms, axis);
+ align_with_z(act_s_1, natoms, axis);
+
+ /* Correlation matrix */
+ Rmat = allocate_square_matrix(3);
+
+ for (i=0; i<natoms; i++)
+ {
+ ref_s_1[i][2]=0.0;
+ act_s_1[i][2]=0.0;
+ }
+
+ /* Weight positions with sqrt(weight) */
+ if (NULL != weight)
+ {
+ weigh_coords(ref_s_1, weight, natoms);
+ weigh_coords(act_s_1, weight, natoms);
+ }
+
+ /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
+ calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
+
+ /* Calculate RtR */
+ RtR = allocate_square_matrix(3);
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ for (k=0; k<3; k++)
+ {
+ RtR[i][j] += Rmat[k][i] * Rmat[k][j];
+ }
+ }
+ }
+ /* Diagonalize RtR */
+ snew(eigvec,3);
+ for (i=0; i<3; i++)
+ snew(eigvec[i],3);
+
+ diagonalize_symmetric(RtR, eigvec, eigval);
+ swap_col(eigvec,0,1);
+ swap_col(eigvec,1,2);
+ swap_val(eigval,0,1);
+ swap_val(eigval,1,2);
+
+ /* Calculate V */
+ for(i=0; i<3; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ V[i][j] = 0.0;
+ WS[i][j] = 0.0;
+ }
+ }
+
+ for (i=0; i<2; i++)
+ for (j=0; j<2; j++)
+ WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
+
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ for (k=0; k<3; k++)
+ {
+ V[i][j] += Rmat[i][k]*WS[k][j];
+ }
+ }
+ }
+ free_square_matrix(Rmat, 3);
+
+ /* Calculate optimal rotation matrix */
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ rot_matrix[i][j] = 0.0;
+
+ for (i=0; i<3; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ for(k=0; k<3; k++){
+ rot_matrix[i][j] += eigvec[i][k]*V[j][k];
+ }
+ }
+ }
+ rot_matrix[2][2] = 1.0;
+
+ /* In some cases abs(rot_matrix[0][0]) can be slighly larger
+ * than unity due to numerical inacurracies. To be able to calculate
+ * the acos function, we put these values back in range. */
+ if (rot_matrix[0][0] > 1.0)
+ {
+ rot_matrix[0][0] = 1.0;
+ }
+ else if (rot_matrix[0][0] < -1.0)
+ {
+ rot_matrix[0][0] = -1.0;
+ }
+
+ /* Determine the optimal rotation angle: */
+ opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
+ if (rot_matrix[0][1] < 0.0)
+ opt_angle = (-1.0)*opt_angle;
+
+ /* Give back some memory */
+ free_square_matrix(RtR, 3);
+ sfree(ref_s_1);
+ sfree(act_s_1);
+ for (i=0; i<3; i++)
+ sfree(eigvec[i]);
+ sfree(eigvec);
+
+ return (real) opt_angle;
+}
+
+
+/* Determine angle of the group by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static real flex_fit_angle(t_rotgrp *rotg)
+{
+ int i;
+ rvec *fitcoords=NULL;
+ rvec center; /* Center of positions passed to the fit routine */
+ real fitangle; /* Angle of the rotation group derived by fitting */
+ rvec coord;
+ real scal;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Get the center of the rotation group.
+ * Note, again, erg->xc has been sorted in do_flexible */
+ get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
+
+ /* === Determine the optimal fit angle for the rotation group === */
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ /* Normalize every position to it's reference length */
+ for (i=0; i<rotg->nat; i++)
+ {
+ /* Put the center of the positions into the origin */
+ rvec_sub(erg->xc[i], center, coord);
+ /* Determine the scaling factor for the length: */
+ scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
+ /* Get position, multiply with the scaling factor and save */
+ svmul(scal, coord, erg->xc_norm[i]);
+ }
+ fitcoords = erg->xc_norm;
+ }
+ else
+ {
+ fitcoords = erg->xc;
+ }
+ /* From the point of view of the current positions, the reference has rotated
+ * backwards. Since we output the angle relative to the fixed reference,
+ * we need the minus sign. */
+ fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
+ rotg->nat, erg->xc_ref_center, center, rotg->vec);
+
+ return fitangle;
+}
+
+
+/* Determine actual angle of each slab by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static void flex_fit_angle_perslab(
+ int g,
+ t_rotgrp *rotg,
+ double t,
+ real degangle,
+ FILE *fp)
+{
+ int i,l,n,islab,ind;
+ rvec curr_x, ref_x;
+ rvec act_center; /* Center of actual positions that are passed to the fit routine */
+ rvec ref_center; /* Same for the reference positions */
+ real fitangle; /* Angle of a slab derived from an RMSD fit to
+ * the reference structure at t=0 */
+ t_gmx_slabdata *sd;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real OOm_av; /* 1/average_mass of a rotation group atom */
+ real m_rel; /* Relative mass of a rotation group atom */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Average mass of a rotation group atom: */
+ OOm_av = erg->invmass*rotg->nat;
+
+ /**********************************/
+ /* First collect the data we need */
+ /**********************************/
+
+ /* Collect the data for the individual slabs */
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+ sd = &(rotg->enfrotgrp->slab_data[islab]);
+ sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
+ ind = 0;
+
+ /* Loop over the relevant atoms in the slab */
+ for (l=erg->firstatom[islab]; l<=erg->lastatom[islab]; l++)
+ {
+ /* Current position of this atom: x[ii][XX/YY/ZZ] */
+ copy_rvec(erg->xc[l], curr_x);
+
+ /* The (unrotated) reference position of this atom is copied to ref_x.
+ * Beware, the xc coords have been sorted in do_flexible */
+ copy_rvec(erg->xc_ref_sorted[l], ref_x);
+
+ /* Save data for doing angular RMSD fit later */
+ /* Save the current atom position */
+ copy_rvec(curr_x, sd->x[ind]);
+ /* Save the corresponding reference position */
+ copy_rvec(ref_x , sd->ref[ind]);
+
+ /* Maybe also mass-weighting was requested. If yes, additionally
+ * multiply the weights with the relative mass of the atom. If not,
+ * multiply with unity. */
+ m_rel = erg->mc_sorted[l]*OOm_av;
+
+ /* Save the weight for this atom in this slab */
+ sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
+
+ /* Next atom in this slab */
+ ind++;
+ }
+ }
+
+ /******************************/
+ /* Now do the fit calculation */
+ /******************************/
+
+ fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
+
+ /* === Now do RMSD fitting for each slab === */
+ /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
+#define SLAB_MIN_ATOMS 4
+
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+ sd = &(rotg->enfrotgrp->slab_data[islab]);
+ if (sd->nat >= SLAB_MIN_ATOMS)
+ {
+ /* Get the center of the slabs reference and current positions */
+ get_center(sd->ref, sd->weight, sd->nat, ref_center);
+ get_center(sd->x , sd->weight, sd->nat, act_center);
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ /* Normalize every position to it's reference length
+ * prior to performing the fit */
+ for (i=0; i<sd->nat;i++) /* Center */
+ {
+ rvec_dec(sd->ref[i], ref_center);
+ rvec_dec(sd->x[i] , act_center);
+ /* Normalize x_i such that it gets the same length as ref_i */
+ svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
+ }
+ /* We already subtracted the centers */
+ clear_rvec(ref_center);
+ clear_rvec(act_center);
+ }
+ fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
+ ref_center, act_center, rotg->vec);
+ fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
+ }
+ }
+ fprintf(fp , "\n");
+
+#undef SLAB_MIN_ATOMS
+}
+
+
+/* Shift x with is */
+static inline void shift_single_coord(matrix box, rvec x, const ivec is)
+{
+ int tx,ty,tz;
+
+
+ tx=is[XX];
+ ty=is[YY];
+ tz=is[ZZ];
+
+ if(TRICLINIC(box))
+ {
+ x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
+ x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
+ x[ZZ] += tz*box[ZZ][ZZ];
+ } else
+ {
+ x[XX] += tx*box[XX][XX];
+ x[YY] += ty*box[YY][YY];
+ x[ZZ] += tz*box[ZZ][ZZ];
+ }
+}
+
+
+/* Determine the 'home' slab of this atom which is the
+ * slab with the highest Gaussian weight of all */
+#define round(a) (int)(a+0.5)
+static inline int get_homeslab(
+ rvec curr_x, /* The position for which the home slab shall be determined */
+ rvec rotvec, /* The rotation vector */
+ real slabdist) /* The slab distance */
+{
+ real dist;
+
+
+ /* The distance of the atom to the coordinate center (where the
+ * slab with index 0) is */
+ dist = iprod(rotvec, curr_x);
+
+ return round(dist / slabdist);
+}
+
+
+/* For a local atom determine the relevant slabs, i.e. slabs in
+ * which the gaussian is larger than min_gaussian
+ */
+static int get_single_atom_gaussians(
+ rvec curr_x,
+ t_commrec *cr,
+ t_rotgrp *rotg)
+{
+ int slab, homeslab;
+ real g;
+ int count = 0;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Determine the 'home' slab of this atom: */
+ homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
+
+ /* First determine the weight in the atoms home slab: */
+ g = gaussian_weight(curr_x, rotg, homeslab);
+
+ erg->gn_atom[count] = g;
+ erg->gn_slabind[count] = homeslab;
+ count++;
+
+
+ /* Determine the max slab */
+ slab = homeslab;
+ while (g > rotg->min_gaussian)
+ {
+ slab++;
+ g = gaussian_weight(curr_x, rotg, slab);
+ erg->gn_slabind[count]=slab;
+ erg->gn_atom[count]=g;
+ count++;
+ }
+ count--;
+
+ /* Determine the max slab */
+ slab = homeslab;
+ do
+ {
+ slab--;
+ g = gaussian_weight(curr_x, rotg, slab);
+ erg->gn_slabind[count]=slab;
+ erg->gn_atom[count]=g;
+ count++;
+ }
+ while (g > rotg->min_gaussian);
+ count--;
+
+ return count;
+}
+
+
+static void flex2_precalc_inner_sum(t_rotgrp *rotg, t_commrec *cr)
+{
+ int i,n,islab;
+ rvec xi; /* positions in the i-sum */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ real gaussian_xi;
+ rvec yi0;
+ rvec rin; /* Helper variables */
+ real fac,fac2;
+ rvec innersumvec;
+ real OOpsii,OOpsiistar;
+ real sin_rin; /* s_ii.r_ii */
+ rvec s_in,tmpvec,tmpvec2;
+ real mi,wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over all slabs that contain something */
+ for (n=erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ /*** D. Calculate the whole inner sum used for second and third sum */
+ /* For slab n, we need to loop over all atoms i again. Since we sorted
+ * the atoms with respect to the rotation vector, we know that it is sufficient
+ * to calculate from firstatom to lastatom only. All other contributions will
+ * be very small. */
+ clear_rvec(innersumvec);
+ for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
+ {
+ /* Coordinate xi of this atom */
+ copy_rvec(erg->xc[i],xi);
+
+ /* The i-weights */
+ gaussian_xi = gaussian_weight(xi,rotg,n);
+ mi = erg->mc_sorted[i]; /* need the sorted mass here */
+ wi = N_M*mi;
+
+ /* Calculate rin */
+ copy_rvec(erg->xc_ref_sorted[i],yi0); /* Reference position yi0 */
+ rvec_sub(yi0, ycn, tmpvec2); /* tmpvec2 = yi0 - ycn */
+ mvmul(erg->rotmat, tmpvec2, rin); /* rin = Omega.(yi0 - ycn) */
+
+ /* Calculate psi_i* and sin */
+ rvec_sub(xi, xcn, tmpvec2); /* tmpvec2 = xi - xcn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x (xi - xcn) */
+ OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
+ OOpsii = norm(tmpvec); /* OOpsii = 1 / psii = |v x (xi - xcn)| */
+
+ /* v x (xi - xcn) */
+ unitv(tmpvec, s_in); /* sin = ---------------- */
+ /* |v x (xi - xcn)| */
+
+ sin_rin=iprod(s_in,rin); /* sin_rin = sin . rin */
+
+ /* Now the whole sum */
+ fac = OOpsii/OOpsiistar;
+ svmul(fac, rin, tmpvec);
+ fac2 = fac*fac*OOpsii;
+ svmul(fac2*sin_rin, s_in, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+
+ svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
+
+ rvec_inc(innersumvec,tmpvec2);
+ } /* now we have the inner sum, used both for sum2 and sum3 */
+
+ /* Save it to be used in do_flex2_lowlevel */
+ copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+ } /* END of loop over slabs */
+}
+
+
+static void flex_precalc_inner_sum(t_rotgrp *rotg, t_commrec *cr)
+{
+ int i,n,islab;
+ rvec xi; /* position */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ rvec qin,rin; /* q_i^n and r_i^n */
+ real bin;
+ rvec tmpvec;
+ rvec innersumvec; /* Inner part of sum_n2 */
+ real gaussian_xi; /* Gaussian weight gn(xi) */
+ real mi,wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over all slabs that contain something */
+ for (n=erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ /* For slab n, we need to loop over all atoms i again. Since we sorted
+ * the atoms with respect to the rotation vector, we know that it is sufficient
+ * to calculate from firstatom to lastatom only. All other contributions will
+ * be very small. */
+ clear_rvec(innersumvec);
+ for (i=erg->firstatom[islab]; i<=erg->lastatom[islab]; i++)
+ {
+ /* Coordinate xi of this atom */
+ copy_rvec(erg->xc[i],xi);
+
+ /* The i-weights */
+ gaussian_xi = gaussian_weight(xi,rotg,n);
+ mi = erg->mc_sorted[i]; /* need the sorted mass here */
+ wi = N_M*mi;
+
+ /* Calculate rin and qin */
+ rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
+ mvmul(erg->rotmat, tmpvec, rin); /* rin = Omega.(yi0 - ycn) */
+ cprod(rotg->vec, rin, tmpvec); /* tmpvec = v x Omega*(yi0-ycn) */
+
+ /* v x Omega*(yi0-ycn) */
+ unitv(tmpvec, qin); /* qin = --------------------- */
+ /* |v x Omega*(yi0-ycn)| */
+
+ /* Calculate bin */
+ rvec_sub(xi, xcn, tmpvec); /* tmpvec = xi-xcn */
+ bin = iprod(qin, tmpvec); /* bin = qin*(xi-xcn) */
+
+ svmul(wi*gaussian_xi*bin, qin, tmpvec);
+
+ /* Add this contribution to the inner sum: */
+ rvec_add(innersumvec, tmpvec, innersumvec);
+ } /* now we have the inner sum vector S^n for this slab */
+ /* Save it to be used in do_flex_lowlevel */
+ copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+ }
+}
+
+
+static real do_flex2_lowlevel(
+ t_rotgrp *rotg,
+ real sigma, /* The Gaussian width sigma */
+ rvec x[],
+ gmx_bool bCalcTorque,
+ matrix box,
+ t_commrec *cr)
+{
+ int count,ic,ii,j,m,n,islab,iigrp;
+ rvec xj; /* position in the i-sum */
+ rvec yj0; /* the reference position in the j-sum */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ real V; /* This node's part of the rotation pot. energy */
+ real gaussian_xj; /* Gaussian weight */
+ real beta;
+
+ real numerator;
+ rvec rjn; /* Helper variables */
+ real fac,fac2;
+
+ real OOpsij,OOpsijstar;
+ real OOsigma2; /* 1/(sigma^2) */
+ real sjn_rjn;
+ real betasigpsi;
+ rvec sjn,tmpvec,tmpvec2;
+ rvec sum1vec_part,sum1vec,sum2vec_part,sum2vec,sum3vec,sum4vec,innersumvec;
+ real sum3,sum4;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real mj,wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ real Wjn; /* g_n(x_j) m_j / Mjn */
+
+ /* To calculate the torque per slab */
+ rvec slab_force; /* Single force from slab n on one atom */
+ rvec slab_sum1vec_part;
+ real slab_sum3part,slab_sum4part;
+ rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Pre-calculate the inner sums, so that we do not have to calculate
+ * them again for every atom */
+ flex2_precalc_inner_sum(rotg, cr);
+
+ /********************************************************/
+ /* Main loop over all local atoms of the rotation group */
+ /********************************************************/
+ N_M = rotg->nat * erg->invmass;
+ V = 0.0;
+ OOsigma2 = 1.0 / (sigma*sigma);
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ]
+ * Note that erg->xc_center contains the center of mass in case the flex2-t
+ * potential was chosen. For the flex2 potential erg->xc_center must be
+ * zero. */
+ rvec_sub(x[ii], erg->xc_center, xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* Determine the slabs to loop over, i.e. the ones with contributions
+ * larger than min_gaussian */
+ count = get_single_atom_gaussians(xj, cr, rotg);
+
+ clear_rvec(sum1vec_part);
+ clear_rvec(sum2vec_part);
+ sum3 = 0.0;
+ sum4 = 0.0;
+ /* Loop over the relevant slabs for this atom */
+ for (ic=0; ic < count; ic++)
+ {
+ n = erg->gn_slabind[ic];
+
+ /* Get the precomputed Gaussian value of curr_slab for curr_x */
+ gaussian_xj = erg->gn_atom[ic];
+
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The (unrotated) reference position of this atom is copied to yj0: */
+ copy_rvec(rotg->x_ref[iigrp], yj0);
+
+ beta = calc_beta(xj, rotg,n);
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ rvec_sub(yj0, ycn, tmpvec2); /* tmpvec2 = yj0 - ycn */
+
+ /* Rotate: */
+ mvmul(erg->rotmat, tmpvec2, rjn); /* rjn = Omega.(yj0 - ycn) */
+
+ /* Subtract the slab center from xj */
+ rvec_sub(xj, xcn, tmpvec2); /* tmpvec2 = xj - xcn */
+
+ /* Calculate sjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x (xj - xcn) */
+
+ OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
+
+ numerator = sqr(iprod(tmpvec, rjn));
+
+ /*********************************/
+ /* Add to the rotation potential */
+ /*********************************/
+ V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
+
+
+ /*************************************/
+ /* Now calculate the force on atom j */
+ /*************************************/
+
+ OOpsij = norm(tmpvec); /* OOpsij = 1 / psij = |v x (xj - xcn)| */
+
+ /* v x (xj - xcn) */
+ unitv(tmpvec, sjn); /* sjn = ---------------- */
+ /* |v x (xj - xcn)| */
+
+ sjn_rjn=iprod(sjn,rjn); /* sjn_rjn = sjn . rjn */
+
+
+ /*** A. Calculate the first of the four sum terms: ****************/
+ fac = OOpsij/OOpsijstar;
+ svmul(fac, rjn, tmpvec);
+ fac2 = fac*fac*OOpsij;
+ svmul(fac2*sjn_rjn, sjn, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ fac2 = wj*gaussian_xj; /* also needed for sum4 */
+ svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
+ /********************/
+ /*** Add to sum1: ***/
+ /********************/
+ rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
+
+ /*** B. Calculate the forth of the four sum terms: ****************/
+ betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
+ /********************/
+ /*** Add to sum4: ***/
+ /********************/
+ slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
+ sum4 += slab_sum4part;
+
+ /*** C. Calculate Wjn for second and third sum */
+ /* Note that we can safely divide by slab_weights since we check in
+ * get_slab_centers that it is non-zero. */
+ Wjn = gaussian_xj*mj/erg->slab_weights[islab];
+
+ /* We already have precalculated the inner sum for slab n */
+ copy_rvec(erg->slab_innersumvec[islab], innersumvec);
+
+ /* Weigh the inner sum vector with Wjn */
+ svmul(Wjn, innersumvec, innersumvec);
+
+ /*** E. Calculate the second of the four sum terms: */
+ /********************/
+ /*** Add to sum2: ***/
+ /********************/
+ rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
+
+ /*** F. Calculate the third of the four sum terms: */
+ slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
+ sum3 += slab_sum3part; /* still needs to be multiplied with v */
+
+ /*** G. Calculate the torque on the local slab's axis: */
+ if (bCalcTorque)
+ {
+ /* Sum1 */
+ cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
+ /* Sum2 */
+ cprod(innersumvec, rotg->vec, slab_sum2vec);
+ /* Sum3 */
+ svmul(slab_sum3part, rotg->vec, slab_sum3vec);
+ /* Sum4 */
+ svmul(slab_sum4part, rotg->vec, slab_sum4vec);
+
+ /* The force on atom ii from slab n only: */
+ for (m=0; m<DIM; m++)
+ slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
+
+ erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
+ }
+ } /* END of loop over slabs */
+
+ /* Construct the four individual parts of the vector sum: */
+ cprod(sum1vec_part, rotg->vec, sum1vec); /* sum1vec = { } x v */
+ cprod(sum2vec_part, rotg->vec, sum2vec); /* sum2vec = { } x v */
+ svmul(sum3, rotg->vec, sum3vec); /* sum3vec = { } . v */
+ svmul(sum4, rotg->vec, sum4vec); /* sum4vec = { } . v */
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ for (m=0; m<DIM; m++)
+ erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
+
+#ifdef SUM_PARTS
+ fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n", -rotg->k*sum1vec[XX], -rotg->k*sum1vec[YY], -rotg->k*sum1vec[ZZ]);
+ fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n", rotg->k*sum2vec[XX], rotg->k*sum2vec[YY], rotg->k*sum2vec[ZZ]);
+ fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n", -rotg->k*sum3vec[XX], -rotg->k*sum3vec[YY], -rotg->k*sum3vec[ZZ]);
+ fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*rotg->k*sum4vec[XX], 0.5*rotg->k*sum4vec[YY], 0.5*rotg->k*sum4vec[ZZ]);
+#endif
+
+ PRINT_FORCE_J
+
+ } /* END of loop over local atoms */
+
+ return V;
+}
+
+
+static real do_flex_lowlevel(
+ t_rotgrp *rotg,
+ real sigma, /* The Gaussian width sigma */
+ rvec x[],
+ gmx_bool bCalcTorque,
+ matrix box,
+ t_commrec *cr)
+{
+ int count,ic,ii,j,m,n,islab,iigrp;
+ rvec xj,yj0; /* current and reference position */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ rvec xj_xcn; /* xj - xcn */
+ rvec qjn; /* q_i^n */
+ rvec sum_n1,sum_n2; /* Two contributions to the rotation force */
+ rvec innersumvec; /* Inner part of sum_n2 */
+ rvec s_n;
+ rvec force_n; /* Single force from slab n on one atom */
+ rvec force_n1,force_n2; /* First and second part of force_n */
+ rvec tmpvec,tmpvec2,tmp_f; /* Helper variables */
+ real V; /* The rotation potential energy */
+ real OOsigma2; /* 1/(sigma^2) */
+ real beta; /* beta_n(xj) */
+ real bjn; /* b_j^n */
+ real gaussian_xj; /* Gaussian weight gn(xj) */
+ real betan_xj_sigma2;
+ real mj,wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Pre-calculate the inner sums, so that we do not have to calculate
+ * them again for every atom */
+ flex_precalc_inner_sum(rotg, cr);
+
+ /********************************************************/
+ /* Main loop over all local atoms of the rotation group */
+ /********************************************************/
+ OOsigma2 = 1.0/(sigma*sigma);
+ N_M = rotg->nat * erg->invmass;
+ V = 0.0;
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ]
+ * Note that erg->xc_center contains the center of mass in case the flex-t
+ * potential was chosen. For the flex potential erg->xc_center must be
+ * zero. */
+ rvec_sub(x[ii], erg->xc_center, xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* Determine the slabs to loop over, i.e. the ones with contributions
+ * larger than min_gaussian */
+ count = get_single_atom_gaussians(xj, cr, rotg);
+
+ clear_rvec(sum_n1);
+ clear_rvec(sum_n2);
+
+ /* Loop over the relevant slabs for this atom */
+ for (ic=0; ic < count; ic++)
+ {
+ n = erg->gn_slabind[ic];
+
+ /* Get the precomputed Gaussian for xj in slab n */
+ gaussian_xj = erg->gn_atom[ic];
+
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The (unrotated) reference position of this atom is saved in yj0: */
+ copy_rvec(rotg->x_ref[iigrp], yj0);
+
+ beta = calc_beta(xj, rotg, n);
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ rvec_sub(yj0, ycn, tmpvec); /* tmpvec = yj0 - ycn */
+
+ /* Rotate: */
+ mvmul(erg->rotmat, tmpvec, tmpvec2); /* tmpvec2 = Omega.(yj0-ycn) */
+
+ /* Subtract the slab center from xj */
+ rvec_sub(xj, xcn, xj_xcn); /* xj_xcn = xj - xcn */
+
+ /* Calculate qjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(xj-xcn) */
+
+ /* v x Omega.(xj-xcn) */
+ unitv(tmpvec,qjn); /* qjn = -------------------- */
+ /* |v x Omega.(xj-xcn)| */
+
+ bjn = iprod(qjn, xj_xcn); /* bjn = qjn * (xj - xcn) */
+
+ /*********************************/
+ /* Add to the rotation potential */
+ /*********************************/
+ V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
+
+ /****************************************************************/
+ /* sum_n1 will typically be the main contribution to the force: */
+ /****************************************************************/
+ betan_xj_sigma2 = beta*OOsigma2; /* beta_n(xj)/sigma^2 */
+
+ /* The next lines calculate
+ * qjn - (bjn*beta(xj)/(2sigma^2))v */
+ svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
+ rvec_sub(qjn,tmpvec2,tmpvec);
+
+ /* Multiply with gn(xj)*bjn: */
+ svmul(gaussian_xj*bjn,tmpvec,tmpvec2);
+
+ /* Sum over n: */
+ rvec_inc(sum_n1,tmpvec2);
+
+ /* We already have precalculated the Sn term for slab n */
+ copy_rvec(erg->slab_innersumvec[islab], s_n);
+ /* beta_n(xj) */
+ svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
+ /* sigma^2 */
+
+ rvec_sub(s_n, tmpvec, innersumvec);
+
+ /* We can safely divide by slab_weights since we check in get_slab_centers
+ * that it is non-zero. */
+ svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
+
+ rvec_add(sum_n2, innersumvec, sum_n2);
+
+ GMX_MPE_LOG(ev_inner_loop_finish);
+
+ /* Calculate the torque: */
+ if (bCalcTorque)
+ {
+ /* The force on atom ii from slab n only: */
+ svmul(-rotg->k*wj, tmpvec2 , force_n1); /* part 1 */
+ svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
+ rvec_add(force_n1, force_n2, force_n);
+ erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
+ }
+ } /* END of loop over slabs */
+
+ /* Put both contributions together: */
+ svmul(wj, sum_n1, sum_n1);
+ svmul(mj, sum_n2, sum_n2);
+ rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ for(m=0; m<DIM; m++)
+ erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
+
+ PRINT_FORCE_J
+
+ } /* END of loop over local atoms */
+
+ return V;
+}
+
+#ifdef PRINT_COORDS
+static void print_coordinates(t_commrec *cr, t_rotgrp *rotg, rvec x[], matrix box, int step)
+{
+ int i;
+ static FILE *fp;
+ static char buf[STRLEN];
+ static gmx_bool bFirst=1;
+
+
+ if (bFirst)
+ {
+ sprintf(buf, "coords%d.txt", cr->nodeid);
+ fp = fopen(buf, "w");
+ bFirst = 0;
+ }
+
+ fprintf(fp, "\nStep %d\n", step);
+ fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
+ box[XX][XX], box[XX][YY], box[XX][ZZ],
+ box[YY][XX], box[YY][YY], box[YY][ZZ],
+ box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
+ for (i=0; i<rotg->nat; i++)
+ {
+ fprintf(fp, "%4d %f %f %f\n", i,
+ erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
+ }
+ fflush(fp);
+
+}
+#endif
+
+
+static int projection_compare(const void *a, const void *b)
+{
+ sort_along_vec_t *xca, *xcb;
+
+
+ xca = (sort_along_vec_t *)a;
+ xcb = (sort_along_vec_t *)b;
+
+ if (xca->xcproj < xcb->xcproj)
+ return -1;
+ else if (xca->xcproj > xcb->xcproj)
+ return 1;
+ else
+ return 0;
+}
+
+
+static void sort_collective_coordinates(
+ t_rotgrp *rotg, /* Rotation group */
+ sort_along_vec_t *data) /* Buffer for sorting the positions */
+{
+ int i;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* The projection of the position vector on the rotation vector is
+ * the relevant value for sorting. Fill the 'data' structure */
+ for (i=0; i<rotg->nat; i++)
+ {
+ data[i].xcproj = iprod(erg->xc[i], rotg->vec); /* sort criterium */
+ data[i].m = erg->mc[i];
+ data[i].ind = i;
+ copy_rvec(erg->xc[i] , data[i].x );
+ copy_rvec(rotg->x_ref[i], data[i].x_ref);
+ }
+ /* Sort the 'data' structure */
+ gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
+
+ /* Copy back the sorted values */
+ for (i=0; i<rotg->nat; i++)
+ {
+ copy_rvec(data[i].x , erg->xc[i] );
+ copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
+ erg->mc_sorted[i] = data[i].m;
+ erg->xc_sortind[i] = data[i].ind;
+ }
+}
+
+
+/* For each slab, get the first and the last index of the sorted atom
+ * indices */
+static void get_firstlast_atom_per_slab(t_rotgrp *rotg, t_commrec *cr)
+{
+ int i,islab,n;
+ real beta;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ GMX_MPE_LOG(ev_get_firstlast_start);
+
+ /* Find the first atom that needs to enter the calculation for each slab */
+ n = erg->slab_first; /* slab */
+ i = 0; /* start with the first atom */
+ do
+ {
+ /* Find the first atom that significantly contributes to this slab */
+ do /* move forward in position until a large enough beta is found */
+ {
+ beta = calc_beta(erg->xc[i], rotg, n);
+ i++;
+ } while ((beta < -erg->max_beta) && (i < rotg->nat));
+ i--;
+ islab = n - erg->slab_first; /* slab index */
+ erg->firstatom[islab] = i;
+ /* Proceed to the next slab */
+ n++;
+ } while (n <= erg->slab_last);
+
+ /* Find the last atom for each slab */
+ n = erg->slab_last; /* start with last slab */
+ i = rotg->nat-1; /* start with the last atom */
+ do
+ {
+ do /* move backward in position until a large enough beta is found */
+ {
+ beta = calc_beta(erg->xc[i], rotg, n);
+ i--;
+ } while ((beta > erg->max_beta) && (i > -1));
+ i++;
+ islab = n - erg->slab_first; /* slab index */
+ erg->lastatom[islab] = i;
+ /* Proceed to the next slab */
+ n--;
+ } while (n >= erg->slab_first);
+
+ GMX_MPE_LOG(ev_get_firstlast_finish);
+}
+
+
+/* Determine the very first and very last slab that needs to be considered
+ * For the first slab that needs to be considered, we have to find the smallest
+ * n that obeys:
+ *
+ * x_first * v - n*Delta_x <= beta_max
+ *
+ * slab index n, slab distance Delta_x, rotation vector v. For the last slab we
+ * have to find the largest n that obeys
+ *
+ * x_last * v - n*Delta_x >= -beta_max
+ *
+ */
+static inline int get_first_slab(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ real max_beta, /* The max_beta value, instead of min_gaussian */
+ rvec firstatom) /* First atom after sorting along the rotation vector v */
+{
+ /* Find the first slab for the first atom */
+ return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
+}
+
+
+static inline int get_last_slab(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ real max_beta, /* The max_beta value, instead of min_gaussian */
+ rvec lastatom) /* Last atom along v */
+{
+ /* Find the last slab for the last atom */
+ return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);
+}
+
+
+static void get_firstlast_slab_check(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ t_gmx_enfrotgrp *erg, /* The rotation group (data only accessible in this file) */
+ rvec firstatom, /* First atom after sorting along the rotation vector v */
+ rvec lastatom, /* Last atom along v */
+ int g, /* The rotation group number */
+ t_commrec *cr)
+{
+ erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
+ erg->slab_last = get_last_slab(rotg, erg->max_beta, lastatom);
+
+ /* Check whether we have reference data to compare against */
+ if (erg->slab_first < erg->slab_first_ref)
+ gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
+ RotStr, erg->slab_first);
+
+ /* Check whether we have reference data to compare against */
+ if (erg->slab_last > erg->slab_last_ref)
+ gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
+ RotStr, erg->slab_last);
+}
+
+
+/* Enforced rotation with a flexible axis */
+static void do_flexible(
+ t_commrec *cr,
+ gmx_enfrot_t enfrot, /* Other rotation data */
+ t_rotgrp *rotg, /* The rotation group */
+ int g, /* Group number */
+ rvec x[], /* The local positions */
+ matrix box,
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int l,nslabs;
+ real sigma; /* The Gaussian width sigma */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Define the sigma value */
+ sigma = 0.7*rotg->slab_dist;
+
+ /* Sort the collective coordinates erg->xc along the rotation vector. This is
+ * an optimization for the inner loop. */
+ sort_collective_coordinates(rotg, enfrot->data);
+
+ /* Determine the first relevant slab for the first atom and the last
+ * relevant slab for the last atom */
+ get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g, cr);
+
+ /* Determine for each slab depending on the min_gaussian cutoff criterium,
+ * a first and a last atom index inbetween stuff needs to be calculated */
+ get_firstlast_atom_per_slab(rotg, cr);
+
+ /* Determine the gaussian-weighted center of positions for all slabs */
+ get_slab_centers(rotg,erg->xc,erg->mc_sorted,cr,g,t,enfrot->out_slabs,bOutstepSlab,FALSE);
+
+ /* Clear the torque per slab from last time step: */
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ for (l=0; l<nslabs; l++)
+ erg->slab_torque_v[l] = 0.0;
+
+ /* Call the rotational forces kernel */
+ GMX_MPE_LOG(ev_flexll_start);
+ if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
+ erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, box, cr);
+ else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
+ erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, box, cr);
+ else
+ gmx_fatal(FARGS, "Unknown flexible rotation type");
+ GMX_MPE_LOG(ev_flexll_finish);
+
+ /* Determine angle by RMSD fit to the reference - Let's hope this */
+ /* only happens once in a while, since this is not parallelized! */
+ if (MASTER(cr))
+ {
+ if (bOutstepRot)
+ {
+ /* Fit angle of the whole rotation group */
+ erg->angle_v = flex_fit_angle(rotg);
+ }
+ if (bOutstepSlab)
+ {
+ /* Fit angle of each slab */
+ flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
+ }
+ }
+
+ /* Lump together the torques from all slabs: */
+ erg->torque_v = 0.0;
+ for (l=0; l<nslabs; l++)
+ erg->torque_v += erg->slab_torque_v[l];
+}
+
+
+/* Calculate the angle between reference and actual rotation group atom,
+ * both projected into a plane perpendicular to the rotation vector: */
+static void angle(t_rotgrp *rotg,
+ rvec x_act,
+ rvec x_ref,
+ real *alpha,
+ real *weight) /* atoms near the rotation axis should count less than atoms far away */
+{
+ rvec xp, xrp; /* current and reference positions projected on a plane perpendicular to pg->vec */
+ rvec dum;
+
+
+ /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
+ /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
+ svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
+ rvec_sub(x_ref, dum, xrp);
+ /* Project x_act: */
+ svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
+ rvec_sub(x_act, dum, xp);
+
+ /* Retrieve information about which vector precedes. gmx_angle always
+ * returns a positive angle. */
+ cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
+
+ if (iprod(rotg->vec, dum) >= 0)
+ *alpha = -gmx_angle(xrp, xp);
+ else
+ *alpha = +gmx_angle(xrp, xp);
+
+ /* Also return the weight */
+ *weight = norm(xp);
+}
+
+
+/* Project first vector onto a plane perpendicular to the second vector
+ * dr = dr - (dr.v)v
+ * Note that v must be of unit length.
+ */
+static inline void project_onto_plane(rvec dr, const rvec v)
+{
+ rvec tmp;
+
+
+ svmul(iprod(dr,v),v,tmp); /* tmp = (dr.v)v */
+ rvec_dec(dr, tmp); /* dr = dr - (dr.v)v */
+}
+
+
+/* Fixed rotation: The rotation reference group rotates around an axis */
+/* The atoms of the actual rotation group are attached with imaginary */
+/* springs to the reference atoms. */
+static void do_fixed(
+ t_commrec *cr,
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bTorque)
+{
+ int j,m;
+ rvec dr;
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec tmpvec;
+
+ /* for mass weighting: */
+ real wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ real k_wi; /* k times wi */
+
+ gmx_bool bProject;
+
+
+ erg=rotg->enfrotgrp;
+ bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Calculate (x_i-x_c) resp. (x_i-u) */
+ rvec_sub(erg->x_loc_pbc[j], erg->xc_center, tmpvec);
+
+ /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+ rvec_sub(erg->xr_loc[j], tmpvec, dr);
+
+ if (bProject)
+ project_onto_plane(dr, rotg->vec);
+
+ /* Mass-weighting */
+ wi = N_M*erg->m_loc[j];
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ k_wi = rotg->k*wi;
+ for (m=0; m<DIM; m++)
+ {
+ tmp_f[m] = k_wi*dr[m];
+ erg->f_rot_loc[j][m] = tmp_f[m];
+ erg->V += 0.5*k_wi*sqr(dr[m]);
+ }
+
+ if (bTorque)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, tmpvec, erg->xr_loc[j], &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+ /* If you want enforced rotation to contribute to the virial,
+ * activate the following lines:
+ if (MASTER(cr))
+ {
+ Add the rotation contribution to the virial
+ for(j=0; j<DIM; j++)
+ for(m=0;m<DIM;m++)
+ vir[j][m] += 0.5*f[ii][j]*dr[m];
+ }
+ */
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+}
+
+
+/* Calculate the radial motion potential and forces */
+static void do_radial_motion(
+ t_commrec *cr,
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bTorque)
+{
+ int j;
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xj_u; /* xj - u */
+ rvec tmpvec;
+ real fac,fac2,sum=0.0;
+ rvec pj;
+
+ /* For mass weighting: */
+ real wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+
+ erg=rotg->enfrotgrp;
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Calculate (xj-u) */
+ rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u); /* xj_u = xj-u */
+
+ /* Calculate Omega.(yj-u) */
+ cprod(rotg->vec, erg->xr_loc[j], tmpvec); /* tmpvec = v x Omega.(yj-u) */
+
+ /* v x Omega.(yj-u) */
+ unitv(tmpvec, pj); /* pj = -------------------- */
+ /* | v x Omega.(yj-u) | */
+
+ fac = iprod(pj, xj_u); /* fac = pj.(xj-u) */
+ fac2 = fac*fac;
+
+ /* Mass-weighting */
+ wj = N_M*erg->m_loc[j];
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*fac, pj, tmp_f);
+ copy_rvec(tmp_f, erg->f_rot_loc[j]);
+ sum += wj*fac2;
+ if (bTorque)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*sum;
+}
+
+
+/* Calculate the radial motion pivot-free potential and forces */
+static void do_radial_motion_pf(
+ t_commrec *cr,
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bTorque)
+{
+ int i,ii,iigrp,j;
+ rvec xj; /* Current position */
+ rvec xj_xc; /* xj - xc */
+ rvec yj0_yc0; /* yj0 - yc0 */
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec tmpvec, tmpvec2;
+ rvec innersumvec; /* Precalculation of the inner sum */
+ rvec innersumveckM;
+ real fac,fac2,V=0.0;
+ rvec qi,qj;
+
+ /* For mass weighting: */
+ real mj,wi,wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+
+ erg=rotg->enfrotgrp;
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Get the current center of the rotation group: */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+ /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
+ clear_rvec(innersumvec);
+ for (i=0; i < rotg->nat; i++)
+ {
+ /* Mass-weighting */
+ wi = N_M*erg->mc[i];
+
+ /* Calculate qi. Note that xc_ref_center has already been subtracted from
+ * x_ref in init_rot_group.*/
+ mvmul(erg->rotmat, rotg->x_ref[i], tmpvec); /* tmpvec = Omega.(yi0-yc0) */
+
+ cprod(rotg->vec, tmpvec, tmpvec2); /* tmpvec2 = v x Omega.(yi0-yc0) */
+
+ /* v x Omega.(yi0-yc0) */
+ unitv(tmpvec2, qi); /* qi = ----------------------- */
+ /* | v x Omega.(yi0-yc0) | */
+
+ rvec_sub(erg->xc[i], erg->xc_center, tmpvec); /* tmpvec = xi-xc */
+
+ svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
+
+ rvec_inc(innersumvec, tmpvec2);
+ }
+ svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ] */
+ copy_rvec(x[ii], xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* The (unrotated) reference position is yj0. yc0 has already
+ * been subtracted in init_rot_group */
+ copy_rvec(rotg->x_ref[iigrp], yj0_yc0); /* yj0_yc0 = yj0 - yc0 */
+
+ /* Calculate Omega.(yj0-yc0) */
+ mvmul(erg->rotmat, yj0_yc0, tmpvec2); /* tmpvec2 = Omega.(yj0 - yc0) */
+
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
+
+ /* v x Omega.(yj0-yc0) */
+ unitv(tmpvec, qj); /* qj = ----------------------- */
+ /* | v x Omega.(yj0-yc0) | */
+
+ /* Calculate (xj-xc) */
+ rvec_sub(xj, erg->xc_center, xj_xc); /* xj_xc = xj-xc */
+
+ fac = iprod(qj, xj_xc); /* fac = qj.(xj-xc) */
+ fac2 = fac*fac;
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
+ svmul(mj, innersumveckM, tmpvec); /* part 2 of force */
+ rvec_inc(tmp_f, tmpvec);
+ copy_rvec(tmp_f, erg->f_rot_loc[j]);
+ V += wj*fac2;
+ if (bTorque)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_xc, yj0_yc0, &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*V;
+}
+
+
+/* Precalculate the inner sum for the radial motion 2 forces */
+static void radial_motion2_precalc_inner_sum(t_rotgrp *rotg, rvec innersumvec)
+{
+ int i;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xi_xc; /* xj - xc */
+ rvec tmpvec,tmpvec2;
+ real fac,fac2;
+ rvec ri,si;
+ real siri;
+ rvec v_xi_xc; /* v x (xj - u) */
+ real psii,psiistar;
+ real wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ rvec sumvec;
+
+ erg=rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over the collective set of positions */
+ clear_rvec(sumvec);
+ for (i=0; i<rotg->nat; i++)
+ {
+ /* Mass-weighting */
+ wi = N_M*erg->mc[i];
+
+ rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc */
+
+ /* Calculate ri. Note that xc_ref_center has already been subtracted from
+ * x_ref in init_rot_group.*/
+ mvmul(erg->rotmat, rotg->x_ref[i], ri); /* ri = Omega.(yi0-yc0) */
+
+ cprod(rotg->vec, xi_xc, v_xi_xc); /* v_xi_xc = v x (xi-u) */
+
+ fac = norm2(v_xi_xc);
+ /* 1 */
+ psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
+ /* |v x (xi-xc)|^2 + eps */
+
+ psii = gmx_invsqrt(fac); /* 1 */
+ /* psii = ------------- */
+ /* |v x (xi-xc)| */
+
+ svmul(psii, v_xi_xc, si); /* si = psii * (v x (xi-xc) ) */
+
+ fac = iprod(v_xi_xc, ri); /* fac = (v x (xi-xc)).ri */
+ fac2 = fac*fac;
+
+ siri = iprod(si, ri); /* siri = si.ri */
+
+ svmul(psiistar/psii, ri, tmpvec);
+ svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ cprod(tmpvec, rotg->vec, tmpvec2);
+
+ svmul(wi*siri, tmpvec2, tmpvec);
+
+ rvec_inc(sumvec, tmpvec);
+ }
+ svmul(rotg->k*erg->invmass, sumvec, innersumvec);
+}
+
+
+/* Calculate the radial motion 2 potential and forces */
+static void do_radial_motion2(
+ t_commrec *cr,
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bTorque)
+{
+ int ii,iigrp,j;
+ rvec xj; /* Position */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xj_u; /* xj - u */
+ rvec tmpvec,tmpvec2;
+ real fac,fac2,Vpart=0.0;
+ rvec rj,sj;
+ real sjrj;
+ rvec v_xj_u; /* v x (xj - u) */
+ real psij,psijstar;
+ real mj,wj; /* For mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_bool bPF;
+ rvec innersumvec;
+
+
+ erg=rotg->enfrotgrp;
+
+ bPF = rotg->eType==erotgRM2PF;
+ clear_rvec(innersumvec);
+ if (bPF)
+ {
+ /* For the pivot-free variant we have to use the current center of
+ * mass of the rotation group instead of the pivot u */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+ /* Also, we precalculate the second term of the forces that is identical
+ * (up to the weight factor mj) for all forces */
+ radial_motion2_precalc_inner_sum(rotg,innersumvec);
+ }
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ if (bPF)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp];
+
+ /* Current position of this atom: x[ii] */
+ copy_rvec(x[ii], xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* The (unrotated) reference position is yj0. yc0 has already
+ * been subtracted in init_rot_group */
+ copy_rvec(rotg->x_ref[iigrp], tmpvec); /* tmpvec = yj0 - yc0 */
+
+ /* Calculate Omega.(yj0-yc0) */
+ mvmul(erg->rotmat, tmpvec, rj); /* rj = Omega.(yj0-yc0) */
+ }
+ else
+ {
+ mj = erg->m_loc[j];
+ copy_rvec(erg->x_loc_pbc[j], xj);
+ copy_rvec(erg->xr_loc[j], rj); /* rj = Omega.(yj0-u) */
+ }
+ /* Mass-weighting */
+ wj = N_M*mj;
+
+ /* Calculate (xj-u) resp. (xj-xc) */
+ rvec_sub(xj, erg->xc_center, xj_u); /* xj_u = xj-u */
+
+ cprod(rotg->vec, xj_u, v_xj_u); /* v_xj_u = v x (xj-u) */
+
+ fac = norm2(v_xj_u);
+ /* 1 */
+ psijstar = 1.0/(fac + rotg->eps); /* psistar = -------------------- */
+ /* |v x (xj-u)|^2 + eps */
+
+ psij = gmx_invsqrt(fac); /* 1 */
+ /* psij = ------------ */
+ /* |v x (xj-u)| */
+
+ svmul(psij, v_xj_u, sj); /* sj = psij * (v x (xj-u) ) */
+
+ fac = iprod(v_xj_u, rj); /* fac = (v x (xj-u)).rj */
+ fac2 = fac*fac;
+
+ sjrj = iprod(sj, rj); /* sjrj = sj.rj */
+
+ svmul(psijstar/psij, rj, tmpvec);
+ svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ cprod(tmpvec, rotg->vec, tmpvec2);
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
+ svmul(mj, innersumvec, tmpvec2); /* This is != 0 only for the pivot-free variant */
+
+ rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
+ Vpart += wj*psijstar*fac2;
+ if (bTorque)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_u, rj, &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*Vpart;
+}
+
+
+/* Determine the smallest and largest position vector (with respect to the
+ * rotation vector) for the reference group */
+static void get_firstlast_atom_ref(
+ t_rotgrp *rotg,
+ int *firstindex,
+ int *lastindex)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int i;
+ real xcproj; /* The projection of a reference position on the
+ rotation vector */
+ real minproj, maxproj; /* Smallest and largest projection on v */
+
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Start with some value */
+ minproj = iprod(rotg->x_ref[0], rotg->vec);
+ maxproj = minproj;
+
+ /* This is just to ensure that it still works if all the atoms of the
+ * reference structure are situated in a plane perpendicular to the rotation
+ * vector */
+ *firstindex = 0;
+ *lastindex = rotg->nat-1;
+
+ /* Loop over all atoms of the reference group,
+ * project them on the rotation vector to find the extremes */
+ for (i=0; i<rotg->nat; i++)
+ {
+ xcproj = iprod(rotg->x_ref[i], rotg->vec);
+ if (xcproj < minproj)
+ {
+ minproj = xcproj;
+ *firstindex = i;
+ }
+ if (xcproj > maxproj)
+ {
+ maxproj = xcproj;
+ *lastindex = i;
+ }
+ }
+}
+
+
+/* Allocate memory for the slabs */
+static void allocate_slabs(
+ t_rotgrp *rotg,
+ FILE *fplog,
+ int g,
+ gmx_bool bVerbose,
+ t_commrec *cr)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int i, nslabs;
+
+
+ erg=rotg->enfrotgrp;
+
+ /* More slabs than are defined for the reference are never needed */
+ nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
+
+ /* Remember how many we allocated */
+ erg->nslabs_alloc = nslabs;
+
+ if (MASTER(cr) && bVerbose)
+ fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
+ RotStr, nslabs,g);
+ snew(erg->slab_center , nslabs);
+ snew(erg->slab_center_ref , nslabs);
+ snew(erg->slab_weights , nslabs);
+ snew(erg->slab_torque_v , nslabs);
+ snew(erg->slab_data , nslabs);
+ snew(erg->gn_atom , nslabs);
+ snew(erg->gn_slabind , nslabs);
+ snew(erg->slab_innersumvec, nslabs);
+ for (i=0; i<nslabs; i++)
+ {
+ snew(erg->slab_data[i].x , rotg->nat);
+ snew(erg->slab_data[i].ref , rotg->nat);
+ snew(erg->slab_data[i].weight, rotg->nat);
+ }
+ snew(erg->xc_ref_sorted, rotg->nat);
+ snew(erg->xc_sortind , rotg->nat);
+ snew(erg->firstatom , nslabs);
+ snew(erg->lastatom , nslabs);
+}
+
+
+/* From the extreme coordinates of the reference group, determine the first
+ * and last slab of the reference. We can never have more slabs in the real
+ * simulation than calculated here for the reference.
+ */
+static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int first,last,firststart;
+ rvec dummy;
+
+
+ erg=rotg->enfrotgrp;
+ first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
+ last = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
+ firststart = first;
+
+ while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+ {
+ first--;
+ }
+ erg->slab_first_ref = first+1;
+ while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+ {
+ last++;
+ }
+ erg->slab_last_ref = last-1;
+
+ erg->slab_buffer = firststart - erg->slab_first_ref;
+}
+
+
+
+static void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
+ rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs, gmx_bool bOutputCenters)
+{
+ int i,ii;
+ rvec coord,*xdum;
+ gmx_bool bFlex,bColl;
+ t_atom *atom;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int ref_firstindex, ref_lastindex;
+ real mass,totalmass;
+
+
+ /* Do we have a flexible axis? */
+ bFlex = ISFLEX(rotg);
+ /* Do we use a global set of coordinates? */
+ bColl = ISCOLL(rotg);
+
+ erg=rotg->enfrotgrp;
+
+ /* Allocate space for collective coordinates if needed */
+ if (bColl)
+ {
+ snew(erg->xc , rotg->nat);
+ snew(erg->xc_shifts , rotg->nat);
+ snew(erg->xc_eshifts, rotg->nat);
+
+ /* Save the original (whole) set of positions such that later the
+ * molecule can always be made whole again */
+ snew(erg->xc_old , rotg->nat);
+ if (MASTER(cr))
+ {
+ for (i=0; i<rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+ copy_rvec(x[ii], erg->xc_old[i]);
+ }
+ }
+#ifdef GMX_MPI
+ if (PAR(cr))
+ gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
+#endif
+
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
+ snew(erg->xc_norm , rotg->nat);
+ }
+ }
+ else
+ {
+ snew(erg->xr_loc , rotg->nat);
+ snew(erg->x_loc_pbc, rotg->nat);
+ }
+
+ snew(erg->f_rot_loc , rotg->nat);
+ snew(erg->xc_ref_ind, rotg->nat);
+
+ /* xc_ref_ind needs to be set to identity in the serial case */
+ if (!PAR(cr))
+ for (i=0; i<rotg->nat; i++)
+ erg->xc_ref_ind[i] = i;
+
+ /* Copy the masses so that the center can be determined. For all types of
+ * enforced rotation, we store the masses in the erg->mc array. */
+ snew(erg->mc, rotg->nat);
+ if (bFlex)
+ snew(erg->mc_sorted, rotg->nat);
+ if (!bColl)
+ snew(erg->m_loc, rotg->nat);
+ totalmass=0.0;
+ for (i=0; i<rotg->nat; i++)
+ {
+ if (rotg->bMassW)
+ {
+ gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
+ mass=atom->m;
+ }
+ else
+ {
+ mass=1.0;
+ }
+ erg->mc[i] = mass;
+ totalmass += mass;
+ }
+ erg->invmass = 1.0/totalmass;
+
+ /* Set xc_ref_center for any rotation potential */
+ if ((rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2))
+ {
+ /* Set the pivot point for the fixed, stationary-axis potentials. This
+ * won't change during the simulation */
+ copy_rvec(rotg->pivot, erg->xc_ref_center);
+ copy_rvec(rotg->pivot, erg->xc_center );
+ }
+ else
+ {
+ /* Center of the reference positions */
+ get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
+
+ /* Center of the actual positions */
+ if (MASTER(cr))
+ {
+ snew(xdum, rotg->nat);
+ for (i=0; i<rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+ copy_rvec(x[ii], xdum[i]);
+ }
+ get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
+ sfree(xdum);
+ }
+#ifdef GMX_MPI
+ if (PAR(cr))
+ gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
+#endif
+ }
+
+ if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
+ {
+ /* Put the reference positions into origin: */
+ for (i=0; i<rotg->nat; i++)
+ rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
+ }
+
+ /* Enforced rotation with flexible axis */
+ if (bFlex)
+ {
+ /* Calculate maximum beta value from minimum gaussian (performance opt.) */
+ erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
+
+ /* Determine the smallest and largest coordinate with respect to the rotation vector */
+ get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
+
+ /* From the extreme coordinates of the reference group, determine the first
+ * and last slab of the reference. */
+ get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
+
+ /* Allocate memory for the slabs */
+ allocate_slabs(rotg, fplog, g, bVerbose, cr);
+
+ /* Flexible rotation: determine the reference centers for the rest of the simulation */
+ erg->slab_first = erg->slab_first_ref;
+ erg->slab_last = erg->slab_last_ref;
+ get_slab_centers(rotg,rotg->x_ref,erg->mc,cr,g,-1,out_slabs,bOutputCenters,TRUE);
+
+ /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ for (i=0; i<rotg->nat; i++)
+ {
+ rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
+ erg->xc_ref_length[i] = norm(coord);
+ }
+ }
+ }
+}
+
+
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot)
+{
+ gmx_ga2la_t ga2la;
+ int g;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+ ga2la = dd->ga2la;
+
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+
+ dd_make_local_group_indices(ga2la,rotg->nat,rotg->ind,
+ &erg->nat_loc,&erg->ind_loc,&erg->nalloc_loc,erg->xc_ref_ind);
+ }
+}
+
+
+extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
+ t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+ gmx_bool bVerbose, unsigned long Flags)
+{
+ t_rot *rot;
+ t_rotgrp *rotg;
+ int g;
+ int nat_max=0; /* Size of biggest rotation group */
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec *x_pbc=NULL; /* Space for the pbc-correct atom positions */
+
+
+ if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
+ gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
+
+ if ( MASTER(cr) && bVerbose)
+ fprintf(stdout, "%s Initializing ...\n", RotStr);
+
+
+ rot = ir->rot;
+ snew(rot->enfrot, 1);
+ er = rot->enfrot;
+ er->Flags = Flags;
+
+ /* When appending, skip first output to avoid duplicate entries in the data files */
+ if (er->Flags & MD_APPENDFILES)
+ er->bOut = FALSE;
+ else
+ er->bOut = TRUE;
+
+ /* Output every step for reruns */
+ if (er->Flags & MD_RERUN)
+ {
+ if (fplog)
+ fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
+ rot->nstrout = 1;
+ rot->nstsout = 1;
+ }
+
+ er->out_slabs = NULL;
+ if ( MASTER(cr) && HaveFlexibleGroups(rot) )
+ er->out_slabs = open_slab_out(opt2fn("-rs",nfile,fnm), rot, oenv);
+
+ if (MASTER(cr))
+ {
+ /* Remove pbc, make molecule whole.
+ * When ir->bContinuation=TRUE this has already been done, but ok. */
+ snew(x_pbc,mtop->natoms);
+ m_rveccopy(mtop->natoms,x,x_pbc);
+ do_pbc_first_mtop(NULL,ir->ePBC,box,mtop,x_pbc);
+ }
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+
+ if (fplog)
+ fprintf(fplog,"%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
+
+ if (rotg->nat > 0)
+ {
+ /* Allocate space for the rotation group's data: */
+ snew(rotg->enfrotgrp, 1);
+ erg = rotg->enfrotgrp;
+
+ nat_max=max(nat_max, rotg->nat);
+
+ if (PAR(cr))
+ {
+ erg->nat_loc = 0;
+ erg->nalloc_loc = 0;
+ erg->ind_loc = NULL;
+ }
+ else
+ {
+ erg->nat_loc = rotg->nat;
+ erg->ind_loc = rotg->ind;
+ }
+ init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs,
+ !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
+ * again if we are appending */
+ }
+ }
+
+ /* Allocate space for enforced rotation buffer variables */
+ er->bufsize = nat_max;
+ snew(er->data, nat_max);
+ snew(er->xbuf, nat_max);
+ snew(er->mbuf, nat_max);
+
+ /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
+ er->mpi_bufsize = 4*rot->ngrp; /* To start with */
+ snew(er->mpi_inbuf , er->mpi_bufsize);
+ snew(er->mpi_outbuf, er->mpi_bufsize);
+
+ /* Only do I/O on the MASTER */
+ er->out_angles = NULL;
+ er->out_rot = NULL;
+ er->out_torque = NULL;
+ if (MASTER(cr))
+ {
+ er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv);
+ if ( HaveFlexibleGroups(rot) )
+ {
+ if (rot->nstrout > 0)
+ er->out_angles = open_angles_out(opt2fn("-ra",nfile,fnm), rot, oenv);
+ if (rot->nstsout > 0)
+ er->out_torque = open_torque_out(opt2fn("-rt",nfile,fnm), rot, oenv);
+ }
+ sfree(x_pbc);
+ }
+}
+
+
+extern void finish_rot(FILE *fplog,t_rot *rot)
+{
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+
+
+ er=rot->enfrot;
+ if (er->out_rot)
+ gmx_fio_fclose(er->out_rot);
+ if (er->out_slabs)
+ gmx_fio_fclose(er->out_slabs);
+ if (er->out_angles)
+ gmx_fio_fclose(er->out_angles);
+ if (er->out_torque)
+ gmx_fio_fclose(er->out_torque);
+}
+
+
+/* Rotate the local reference positions and store them in
+ * erg->xr_loc[0...(nat_loc-1)]
+ *
+ * Note that we already subtracted u or y_c from the reference positions
+ * in init_rot_group().
+ */
+static void rotate_local_reference(t_rotgrp *rotg)
+{
+ gmx_enfrotgrp_t erg;
+ int i,ii;
+
+
+ erg=rotg->enfrotgrp;
+
+ for (i=0; i<erg->nat_loc; i++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ ii = erg->xc_ref_ind[i];
+ /* Rotate */
+ mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
+ }
+}
+
+
+/* Select the PBC representation for each local x position and store that
+ * for later usage. We assume the right PBC image of an x is the one nearest to
+ * its rotated reference */
+static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
+{
+ int d,i,ii,m;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xref,xcurr,dx;
+ ivec shift;
+
+
+ erg=rotg->enfrotgrp;
+
+ for (i=0; i<erg->nat_loc; i++)
+ {
+ clear_ivec(shift);
+
+ /* Index of a rotation group atom */
+ ii = erg->ind_loc[i];
+
+ /* Get the reference position. The pivot was already
+ * subtracted in init_rot_group() from the reference positions. Also,
+ * the reference positions have already been rotated in
+ * rotate_local_reference() */
+ copy_rvec(erg->xr_loc[i], xref);
+
+ /* Subtract the (old) center from the current positions
+ * (just to determine the shifts!) */
+ rvec_sub(x[ii], erg->xc_center, xcurr);
+
+ /* Shortest PBC distance between the atom and its reference */
+ rvec_sub(xcurr, xref, dx);
+
+ /* Determine the shift for this atom */
+ for(m=npbcdim-1; m>=0; m--)
+ {
+ while (dx[m] < -0.5*box[m][m])
+ {
+ for(d=0; d<DIM; d++)
+ dx[d] += box[m][d];
+ shift[m]++;
+ }
+ while (dx[m] >= 0.5*box[m][m])
+ {
+ for(d=0; d<DIM; d++)
+ dx[d] -= box[m][d];
+ shift[m]--;
+ }
+ }
+
+ /* Apply the shift to the current atom */
+ copy_rvec(x[ii], erg->x_loc_pbc[i]);
+ shift_single_coord(box, erg->x_loc_pbc[i], shift);
+ }
+}
+
+
+extern void do_rotation(
+ t_commrec *cr,
+ t_inputrec *ir,
+ matrix box,
+ rvec x[],
+ real t,
+ gmx_large_int_t step,
+ gmx_wallcycle_t wcycle,
+ gmx_bool bNS)
+{
+ int g,i,ii;
+ t_rot *rot;
+ t_rotgrp *rotg;
+ gmx_bool outstep_slab, outstep_rot;
+ gmx_bool bFlex,bColl;
+ float cycles_rot;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec transvec;
+#ifdef TAKETIME
+ double t0;
+#endif
+
+
+ rot=ir->rot;
+ er=rot->enfrot;
+
+ /* When to output in main rotation output file */
+ outstep_rot = do_per_step(step, rot->nstrout) && er->bOut;
+ /* When to output per-slab data */
+ outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
+
+ /* Output time into rotation output file */
+ if (outstep_rot && MASTER(cr))
+ fprintf(er->out_rot, "%12.3e",t);
+
+ /**************************************************************************/
+ /* First do ALL the communication! */
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+
+ /* Do we have a flexible axis? */
+ bFlex = ISFLEX(rotg);
+ /* Do we use a collective (global) set of coordinates? */
+ bColl = ISCOLL(rotg);
+
+ /* Calculate the rotation matrix for this angle: */
+ erg->degangle = rotg->rate * t;
+ calc_rotmat(rotg->vec,erg->degangle,erg->rotmat);
+
+ if (bColl)
+ {
+ /* Transfer the rotation group's positions such that every node has
+ * all of them. Every node contributes its local positions x and stores
+ * it in the collective erg->xc array. */
+ communicate_group_positions(cr,erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
+ x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
+ }
+ else
+ {
+ /* Fill the local masses array;
+ * this array changes in DD/neighborsearching steps */
+ if (bNS)
+ {
+ for (i=0; i<erg->nat_loc; i++)
+ {
+ /* Index of local atom w.r.t. the collective rotation group */
+ ii = erg->xc_ref_ind[i];
+ erg->m_loc[i] = erg->mc[ii];
+ }
+ }
+
+ /* Calculate Omega*(y_i-y_c) for the local positions */
+ rotate_local_reference(rotg);
+
+ /* Choose the nearest PBC images of the group atoms with respect
+ * to the rotated reference positions */
+ choose_pbc_image(x, rotg, box, 3);
+
+ /* Get the center of the rotation group */
+ if ( (rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) )
+ get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
+ }
+
+ } /* End of loop over rotation groups */
+
+ /**************************************************************************/
+ /* Done communicating, we can start to count cycles now ... */
+ wallcycle_start(wcycle, ewcROT);
+ GMX_MPE_LOG(ev_rotcycles_start);
+
+#ifdef TAKETIME
+ t0 = MPI_Wtime();
+#endif
+
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+
+ bFlex = ISFLEX(rotg);
+ bColl = ISCOLL(rotg);
+
+ if (outstep_rot && MASTER(cr))
+ fprintf(er->out_rot, "%12.4f", erg->degangle);
+
+ /* Clear values from last time step */
+ erg->V = 0.0;
+ erg->torque_v = 0.0;
+ erg->angle_v = 0.0;
+ erg->weight_v = 0.0;
+
+ switch(rotg->eType)
+ {
+ case erotgISO:
+ case erotgISOPF:
+ case erotgPM:
+ case erotgPMPF:
+ do_fixed(cr,rotg,x,box,t,step,outstep_rot);
+ break;
+ case erotgRM:
+ do_radial_motion(cr,rotg,x,box,t,step,outstep_rot);
+ break;
+ case erotgRMPF:
+ do_radial_motion_pf(cr,rotg,x,box,t,step,outstep_rot);
+ break;
+ case erotgRM2:
+ case erotgRM2PF:
+ do_radial_motion2(cr,rotg,x,box,t,step,outstep_rot);
+ break;
+ case erotgFLEXT:
+ case erotgFLEX2T:
+ /* Subtract the center of the rotation group from the collective positions array
+ * Also store the center in erg->xc_center since it needs to be subtracted
+ * in the low level routines from the local coordinates as well */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+ svmul(-1.0, erg->xc_center, transvec);
+ translate_x(erg->xc, rotg->nat, transvec);
+ do_flexible(cr,er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ case erotgFLEX:
+ case erotgFLEX2:
+ /* Do NOT subtract the center of mass in the low level routines! */
+ clear_rvec(erg->xc_center);
+ do_flexible(cr,er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ default:
+ gmx_fatal(FARGS, "No such rotation potential.");
+ break;
+ }
+ }
+
+#ifdef TAKETIME
+ if (MASTER(cr))
+ fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
+#endif
+
+ /* Stop the cycle counter and add to the force cycles for load balancing */
+ cycles_rot = wallcycle_stop(wcycle,ewcROT);
+ if (DOMAINDECOMP(cr) && wcycle)
+ dd_cycles_add(cr->dd,cycles_rot,ddCyclF);
+ GMX_MPE_LOG(ev_rotcycles_finish);
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef GMX_CRAY_XT3
+#include<catamount/dclock.h>
+#endif
+
+
+#include <stdio.h>
+#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <math.h>
+#include "typedefs.h"
+#include "string2.h"
+#include "gmxfio.h"
+#include "smalloc.h"
+#include "names.h"
+#include "confio.h"
+#include "mvdata.h"
+#include "txtdump.h"
+#include "pbc.h"
+#include "chargegroup.h"
+#include "vec.h"
+#include "time.h"
+#include "nrnb.h"
+#include "mshift.h"
+#include "mdrun.h"
+#include "update.h"
+#include "physics.h"
+#include "main.h"
+#include "mdatoms.h"
+#include "force.h"
+#include "bondf.h"
+#include "pme.h"
+#include "pppm.h"
+#include "disre.h"
+#include "orires.h"
+#include "network.h"
+#include "calcmu.h"
+#include "constr.h"
+#include "xvgr.h"
+#include "trnio.h"
+#include "xtcio.h"
+#include "copyrite.h"
+#include "pull_rotation.h"
+#include "mpelogging.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "gmx_wallcycle.h"
+#include "genborn.h"
+
+#ifdef GMX_LIB_MPI
+#include <mpi.h>
+#endif
+#ifdef GMX_THREADS
+#include "tmpi.h"
+#endif
+
+#include "qmmm.h"
+
+#if 0
+typedef struct gmx_timeprint {
+
+} t_gmx_timeprint;
+#endif
+
+/* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
+char *
+gmx_ctime_r(const time_t *clock,char *buf, int n);
+
+
+double
+gmx_gettime()
+{
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval t;
+ struct timezone tz = { 0,0 };
+ double seconds;
+
+ gettimeofday(&t,&tz);
+
+ seconds = (double) t.tv_sec + 1e-6*(double)t.tv_usec;
+
+ return seconds;
+#else
+ double seconds;
+
+ seconds = time(NULL);
+
+ return seconds;
+#endif
+}
+
+
+#define difftime(end,start) ((double)(end)-(double)(start))
+
+void print_time(FILE *out,gmx_runtime_t *runtime,gmx_large_int_t step,
+ t_inputrec *ir, t_commrec *cr)
+{
+ time_t finish;
+ char timebuf[STRLEN];
+ double dt;
+ char buf[48];
+
+#ifndef GMX_THREADS
+ if (!PAR(cr))
+#endif
+ {
+ fprintf(out,"\r");
+ }
+ fprintf(out,"step %s",gmx_step_str(step,buf));
+ if ((step >= ir->nstlist))
+ {
+ if ((ir->nstlist == 0) || ((step % ir->nstlist) == 0))
+ {
+ /* We have done a full cycle let's update time_per_step */
+ runtime->last = gmx_gettime();
+ dt = difftime(runtime->last,runtime->real);
+ runtime->time_per_step = dt/(step - ir->init_step + 1);
+ }
+ dt = (ir->nsteps + ir->init_step - step)*runtime->time_per_step;
+
+ if (ir->nsteps >= 0)
+ {
+ if (dt >= 300)
+ {
+ finish = (time_t) (runtime->last + dt);
+ gmx_ctime_r(&finish,timebuf,STRLEN);
+ sprintf(buf,"%s",timebuf);
+ buf[strlen(buf)-1]='\0';
+ fprintf(out,", will finish %s",buf);
+ }
+ else
+ fprintf(out,", remaining runtime: %5d s ",(int)dt);
+ }
+ else
+ {
+ fprintf(out," performance: %.1f ns/day ",
+ ir->delta_t/1000*24*60*60/runtime->time_per_step);
+ }
+ }
+#ifndef GMX_THREADS
+ if (PAR(cr))
+ {
+ fprintf(out,"\n");
+ }
+#endif
+
+ fflush(out);
+}
+
+#ifdef NO_CLOCK
+#define clock() -1
+#endif
+
+static double set_proctime(gmx_runtime_t *runtime)
+{
+ double diff;
+#ifdef GMX_CRAY_XT3
+ double prev;
+
+ prev = runtime->proc;
+ runtime->proc = dclock();
+
+ diff = runtime->proc - prev;
+#else
+ clock_t prev;
+
+ prev = runtime->proc;
+ runtime->proc = clock();
+
+ diff = (double)(runtime->proc - prev)/(double)CLOCKS_PER_SEC;
+#endif
+ if (diff < 0)
+ {
+ /* The counter has probably looped, ignore this data */
+ diff = 0;
+ }
+
+ return diff;
+}
+
+void runtime_start(gmx_runtime_t *runtime)
+{
+ runtime->real = gmx_gettime();
+ runtime->proc = 0;
+ set_proctime(runtime);
+ runtime->realtime = 0;
+ runtime->proctime = 0;
+ runtime->last = 0;
+ runtime->time_per_step = 0;
+}
+
+void runtime_end(gmx_runtime_t *runtime)
+{
+ double now;
+
+ now = gmx_gettime();
+
+ runtime->proctime += set_proctime(runtime);
+ runtime->realtime = now - runtime->real;
+ runtime->real = now;
+}
+
+void runtime_upd_proc(gmx_runtime_t *runtime)
+{
+ runtime->proctime += set_proctime(runtime);
+}
+
+void print_date_and_time(FILE *fplog,int nodeid,const char *title,
+ const gmx_runtime_t *runtime)
+{
+ int i;
+ char timebuf[STRLEN];
+ char time_string[STRLEN];
+ time_t tmptime;
+
+ if (fplog)
+ {
+ if (runtime != NULL)
+ {
+ tmptime = (time_t) runtime->real;
+ gmx_ctime_r(&tmptime,timebuf,STRLEN);
+ }
+ else
+ {
+ tmptime = (time_t) gmx_gettime();
+ gmx_ctime_r(&tmptime,timebuf,STRLEN);
+ }
+ for(i=0; timebuf[i]>=' '; i++)
+ {
+ time_string[i]=timebuf[i];
+ }
+ time_string[i]='\0';
+
+ fprintf(fplog,"%s on node %d %s\n",title,nodeid,time_string);
+ }
+}
+
+static void sum_forces(int start,int end,rvec f[],rvec flr[])
+{
+ int i;
+
+ if (gmx_debug_at) {
+ pr_rvecs(debug,0,"fsr",f+start,end-start);
+ pr_rvecs(debug,0,"flr",flr+start,end-start);
+ }
+ for(i=start; (i<end); i++)
+ rvec_inc(f[i],flr[i]);
+}
+
+/*
+ * calc_f_el calculates forces due to an electric field.
+ *
+ * force is kJ mol^-1 nm^-1 = e * kJ mol^-1 nm^-1 / e
+ *
+ * Et[] contains the parameters for the time dependent
+ * part of the field (not yet used).
+ * Ex[] contains the parameters for
+ * the spatial dependent part of the field. You can have cool periodic
+ * fields in principle, but only a constant field is supported
+ * now.
+ * The function should return the energy due to the electric field
+ * (if any) but for now returns 0.
+ *
+ * WARNING:
+ * There can be problems with the virial.
+ * Since the field is not self-consistent this is unavoidable.
+ * For neutral molecules the virial is correct within this approximation.
+ * For neutral systems with many charged molecules the error is small.
+ * But for systems with a net charge or a few charged molecules
+ * the error can be significant when the field is high.
+ * Solution: implement a self-consitent electric field into PME.
+ */
+static void calc_f_el(FILE *fp,int start,int homenr,
+ real charge[],rvec x[],rvec f[],
+ t_cosines Ex[],t_cosines Et[],double t)
+{
+ rvec Ext;
+ real t0;
+ int i,m;
+
+ for(m=0; (m<DIM); m++)
+ {
+ if (Et[m].n > 0)
+ {
+ if (Et[m].n == 3)
+ {
+ t0 = Et[m].a[1];
+ Ext[m] = cos(Et[m].a[0]*(t-t0))*exp(-sqr(t-t0)/(2.0*sqr(Et[m].a[2])));
+ }
+ else
+ {
+ Ext[m] = cos(Et[m].a[0]*t);
+ }
+ }
+ else
+ {
+ Ext[m] = 1.0;
+ }
+ if (Ex[m].n > 0)
+ {
+ /* Convert the field strength from V/nm to MD-units */
+ Ext[m] *= Ex[m].a[0]*FIELDFAC;
+ for(i=start; (i<start+homenr); i++)
+ f[i][m] += charge[i]*Ext[m];
+ }
+ else
+ {
+ Ext[m] = 0;
+ }
+ }
+ if (fp != NULL)
+ {
+ fprintf(fp,"%10g %10g %10g %10g #FIELD\n",t,
+ Ext[XX]/FIELDFAC,Ext[YY]/FIELDFAC,Ext[ZZ]/FIELDFAC);
+ }
+}
+
+static void calc_virial(FILE *fplog,int start,int homenr,rvec x[],rvec f[],
+ tensor vir_part,t_graph *graph,matrix box,
+ t_nrnb *nrnb,const t_forcerec *fr,int ePBC)
+{
+ int i,j;
+ tensor virtest;
+
+ /* The short-range virial from surrounding boxes */
+ clear_mat(vir_part);
+ calc_vir(fplog,SHIFTS,fr->shift_vec,fr->fshift,vir_part,ePBC==epbcSCREW,box);
+ inc_nrnb(nrnb,eNR_VIRIAL,SHIFTS);
+
+ /* Calculate partial virial, for local atoms only, based on short range.
+ * Total virial is computed in global_stat, called from do_md
+ */
+ f_calc_vir(fplog,start,start+homenr,x,f,vir_part,graph,box);
+ inc_nrnb(nrnb,eNR_VIRIAL,homenr);
+
+ /* Add position restraint contribution */
+ for(i=0; i<DIM; i++) {
+ vir_part[i][i] += fr->vir_diag_posres[i];
+ }
+
+ /* Add wall contribution */
+ for(i=0; i<DIM; i++) {
+ vir_part[i][ZZ] += fr->vir_wall_z[i];
+ }
+
+ if (debug)
+ pr_rvecs(debug,0,"vir_part",vir_part,DIM);
+}
+
+static void print_large_forces(FILE *fp,t_mdatoms *md,t_commrec *cr,
+ gmx_large_int_t step,real pforce,rvec *x,rvec *f)
+{
+ int i;
+ real pf2,fn2;
+ char buf[STEPSTRSIZE];
+
+ pf2 = sqr(pforce);
+ for(i=md->start; i<md->start+md->homenr; i++) {
+ fn2 = norm2(f[i]);
+ /* We also catch NAN, if the compiler does not optimize this away. */
+ if (fn2 >= pf2 || fn2 != fn2) {
+ fprintf(fp,"step %s atom %6d x %8.3f %8.3f %8.3f force %12.5e\n",
+ gmx_step_str(step,buf),
+ ddglatnr(cr->dd,i),x[i][XX],x[i][YY],x[i][ZZ],sqrt(fn2));
+ }
+ }
+}
+
+void do_force(FILE *fplog,t_commrec *cr,
+ t_inputrec *inputrec,
+ gmx_large_int_t step,t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_localtop_t *top,
+ gmx_mtop_t *mtop,
+ gmx_groups_t *groups,
+ matrix box,rvec x[],history_t *hist,
+ rvec f[],
+ tensor vir_force,
+ t_mdatoms *mdatoms,
+ gmx_enerdata_t *enerd,t_fcdata *fcd,
+ real lambda,t_graph *graph,
+ t_forcerec *fr,gmx_vsite_t *vsite,rvec mu_tot,
+ double t,FILE *field,gmx_edsam_t ed,
+ gmx_bool bBornRadii,
+ int flags)
+{
+ int cg0,cg1,i,j;
+ int start,homenr;
+ double mu[2*DIM];
+ gmx_bool bSepDVDL,bStateChanged,bNS,bFillGrid,bCalcCGCM,bBS;
+ gmx_bool bDoLongRange,bDoForces,bSepLRF;
+ matrix boxs;
+ real e,v,dvdl;
+ t_pbc pbc;
+ float cycles_ppdpme,cycles_pme,cycles_seppme,cycles_force;
+
+ start = mdatoms->start;
+ homenr = mdatoms->homenr;
+
+ bSepDVDL = (fr->bSepDVDL && do_per_step(step,inputrec->nstlog));
+
+ clear_mat(vir_force);
+
+ if (PARTDECOMP(cr))
+ {
+ pd_cg_range(cr,&cg0,&cg1);
+ }
+ else
+ {
+ cg0 = 0;
+ if (DOMAINDECOMP(cr))
+ {
+ cg1 = cr->dd->ncg_tot;
+ }
+ else
+ {
+ cg1 = top->cgs.nr;
+ }
+ if (fr->n_tpi > 0)
+ {
+ cg1--;
+ }
+ }
+
+ bStateChanged = (flags & GMX_FORCE_STATECHANGED);
+ bNS = (flags & GMX_FORCE_NS) && (fr->bAllvsAll==FALSE);
+ bFillGrid = (bNS && bStateChanged);
+ bCalcCGCM = (bFillGrid && !DOMAINDECOMP(cr));
+ bDoLongRange = (fr->bTwinRange && bNS && (flags & GMX_FORCE_DOLR));
+ bDoForces = (flags & GMX_FORCE_FORCES);
+ bSepLRF = (bDoLongRange && bDoForces && (flags & GMX_FORCE_SEPLRF));
+
+ if (bStateChanged)
+ {
+ update_forcerec(fplog,fr,box);
+
+ /* Calculate total (local) dipole moment in a temporary common array.
+ * This makes it possible to sum them over nodes faster.
+ */
+ calc_mu(start,homenr,
+ x,mdatoms->chargeA,mdatoms->chargeB,mdatoms->nChargePerturbed,
+ mu,mu+DIM);
+ }
+
+ if (fr->ePBC != epbcNONE) {
+ /* Compute shift vectors every step,
+ * because of pressure coupling or box deformation!
+ */
+ if ((flags & GMX_FORCE_DYNAMICBOX) && bStateChanged)
+ calc_shifts(box,fr->shift_vec);
+
+ if (bCalcCGCM) {
+ put_charge_groups_in_box(fplog,cg0,cg1,fr->ePBC,box,
+ &(top->cgs),x,fr->cg_cm);
+ inc_nrnb(nrnb,eNR_CGCM,homenr);
+ inc_nrnb(nrnb,eNR_RESETX,cg1-cg0);
+ }
+ else if (EI_ENERGY_MINIMIZATION(inputrec->eI) && graph) {
+ unshift_self(graph,box,x);
+ }
+ }
+ else if (bCalcCGCM) {
+ calc_cgcm(fplog,cg0,cg1,&(top->cgs),x,fr->cg_cm);
+ inc_nrnb(nrnb,eNR_CGCM,homenr);
+ }
+
+ if (bCalcCGCM) {
+ if (PAR(cr)) {
+ move_cgcm(fplog,cr,fr->cg_cm);
+ }
+ if (gmx_debug_at)
+ pr_rvecs(debug,0,"cgcm",fr->cg_cm,top->cgs.nr);
+ }
+
+#ifdef GMX_MPI
+ if (!(cr->duty & DUTY_PME)) {
+ /* Send particle coordinates to the pme nodes.
+ * Since this is only implemented for domain decomposition
+ * and domain decomposition does not use the graph,
+ * we do not need to worry about shifting.
+ */
+
+ wallcycle_start(wcycle,ewcPP_PMESENDX);
+ GMX_MPE_LOG(ev_send_coordinates_start);
+
+ bBS = (inputrec->nwall == 2);
+ if (bBS) {
+ copy_mat(box,boxs);
+ svmul(inputrec->wall_ewald_zfac,boxs[ZZ],boxs[ZZ]);
+ }
+
+ gmx_pme_send_x(cr,bBS ? boxs : box,x,
+ mdatoms->nChargePerturbed,lambda,
+ ( flags & GMX_FORCE_VIRIAL),step);
+
+ GMX_MPE_LOG(ev_send_coordinates_finish);
+ wallcycle_stop(wcycle,ewcPP_PMESENDX);
+ }
+#endif /* GMX_MPI */
+
+ /* Communicate coordinates and sum dipole if necessary */
+ if (PAR(cr))
+ {
+ wallcycle_start(wcycle,ewcMOVEX);
+ if (DOMAINDECOMP(cr))
+ {
+ dd_move_x(cr->dd,box,x);
+ }
+ else
+ {
+ move_x(fplog,cr,GMX_LEFT,GMX_RIGHT,x,nrnb);
+ }
+ /* When we don't need the total dipole we sum it in global_stat */
+ if (bStateChanged && NEED_MUTOT(*inputrec))
+ {
+ gmx_sumd(2*DIM,mu,cr);
+ }
+ wallcycle_stop(wcycle,ewcMOVEX);
+ }
+ if (bStateChanged)
+ {
+ for(i=0; i<2; i++)
+ {
+ for(j=0;j<DIM;j++)
+ {
+ fr->mu_tot[i][j] = mu[i*DIM + j];
+ }
+ }
+ }
+ if (fr->efep == efepNO)
+ {
+ copy_rvec(fr->mu_tot[0],mu_tot);
+ }
+ else
+ {
+ for(j=0; j<DIM; j++)
+ {
+ mu_tot[j] =
+ (1.0 - lambda)*fr->mu_tot[0][j] + lambda*fr->mu_tot[1][j];
+ }
+ }
+
+ /* Reset energies */
+ reset_enerdata(&(inputrec->opts),fr,bNS,enerd,MASTER(cr));
+ clear_rvecs(SHIFTS,fr->fshift);
+
+ if (bNS)
+ {
+ wallcycle_start(wcycle,ewcNS);
+
+ if (graph && bStateChanged)
+ {
+ /* Calculate intramolecular shift vectors to make molecules whole */
+ mk_mshift(fplog,graph,fr->ePBC,box,x);
+ }
+
+ /* Reset long range forces if necessary */
+ if (fr->bTwinRange)
+ {
+ /* Reset the (long-range) forces if necessary */
+ clear_rvecs(fr->natoms_force_constr,bSepLRF ? fr->f_twin : f);
+ }
+
+ /* Do the actual neighbour searching and if twin range electrostatics
+ * also do the calculation of long range forces and energies.
+ */
+ dvdl = 0;
+ ns(fplog,fr,x,box,
+ groups,&(inputrec->opts),top,mdatoms,
+ cr,nrnb,lambda,&dvdl,&enerd->grpp,bFillGrid,
+ bDoLongRange,bDoForces,bSepLRF ? fr->f_twin : f);
+ if (bSepDVDL)
+ {
+ fprintf(fplog,sepdvdlformat,"LR non-bonded",0.0,dvdl);
+ }
+ enerd->dvdl_lin += dvdl;
+
+ wallcycle_stop(wcycle,ewcNS);
+ }
+
+ if (inputrec->implicit_solvent && bNS)
+ {
+ make_gb_nblist(cr,inputrec->gb_algorithm,inputrec->rlist,
+ x,box,fr,&top->idef,graph,fr->born);
+ }
+
+ if (DOMAINDECOMP(cr))
+ {
+ if (!(cr->duty & DUTY_PME))
+ {
+ wallcycle_start(wcycle,ewcPPDURINGPME);
+ dd_force_flop_start(cr->dd,nrnb);
+ }
+ }
+
+ if (inputrec->bRot)
+ {
+ /* Enforced rotation has its own cycle counter that starts after the collective
+ * coordinates have been communicated. It is added to ddCyclF */
+ do_rotation(cr,inputrec,box,x,t,step,wcycle,bNS);
+ }
+
+ /* Start the force cycle counter.
+ * This counter is stopped in do_forcelow_level.
+ * No parallel communication should occur while this counter is running,
+ * since that will interfere with the dynamic load balancing.
+ */
+ wallcycle_start(wcycle,ewcFORCE);
+ GMX_MPE_LOG(ev_forcecycles_start);
+
+ if (bDoForces)
+ {
+ /* Reset forces for which the virial is calculated separately:
+ * PME/Ewald forces if necessary */
+ if (fr->bF_NoVirSum)
+ {
+ if (flags & GMX_FORCE_VIRIAL)
+ {
+ fr->f_novirsum = fr->f_novirsum_alloc;
+ GMX_BARRIER(cr->mpi_comm_mygroup);
+ if (fr->bDomDec)
+ {
+ clear_rvecs(fr->f_novirsum_n,fr->f_novirsum);
+ }
+ else
+ {
+ clear_rvecs(homenr,fr->f_novirsum+start);
+ }
+ GMX_BARRIER(cr->mpi_comm_mygroup);
+ }
+ else
+ {
+ /* We are not calculating the pressure so we do not need
+ * a separate array for forces that do not contribute
+ * to the pressure.
+ */
+ fr->f_novirsum = f;
+ }
+ }
+
+ if (bSepLRF)
+ {
+ /* Add the long range forces to the short range forces */
+ for(i=0; i<fr->natoms_force_constr; i++)
+ {
+ copy_rvec(fr->f_twin[i],f[i]);
+ }
+ }
+ else if (!(fr->bTwinRange && bNS))
+ {
+ /* Clear the short-range forces */
+ clear_rvecs(fr->natoms_force_constr,f);
+ }
+
+ clear_rvec(fr->vir_diag_posres);
+
+ GMX_BARRIER(cr->mpi_comm_mygroup);
+ }
+ if (inputrec->ePull == epullCONSTRAINT)
+ {
+ clear_pull_forces(inputrec->pull);
+ }
+
+ /* update QMMMrec, if necessary */
+ if(fr->bQMMM)
+ {
+ update_QMMMrec(cr,fr,x,mdatoms,box,top);
+ }
+
+ if ((flags & GMX_FORCE_BONDED) && top->idef.il[F_POSRES].nr > 0)
+ {
+ /* Position restraints always require full pbc */
+ set_pbc(&pbc,inputrec->ePBC,box);
+ v = posres(top->idef.il[F_POSRES].nr,top->idef.il[F_POSRES].iatoms,
+ top->idef.iparams_posres,
+ (const rvec*)x,fr->f_novirsum,fr->vir_diag_posres,
+ inputrec->ePBC==epbcNONE ? NULL : &pbc,lambda,&dvdl,
+ fr->rc_scaling,fr->ePBC,fr->posres_com,fr->posres_comB);
+ if (bSepDVDL)
+ {
+ fprintf(fplog,sepdvdlformat,
+ interaction_function[F_POSRES].longname,v,dvdl);
+ }
+ enerd->term[F_POSRES] += v;
+ /* This linear lambda dependence assumption is only correct
+ * when only k depends on lambda,
+ * not when the reference position depends on lambda.
+ * grompp checks for this.
+ */
+ enerd->dvdl_lin += dvdl;
+ inc_nrnb(nrnb,eNR_POSRES,top->idef.il[F_POSRES].nr/2);
+ }
+
+ /* Compute the bonded and non-bonded energies and optionally forces */
+ do_force_lowlevel(fplog,step,fr,inputrec,&(top->idef),
+ cr,nrnb,wcycle,mdatoms,&(inputrec->opts),
+ x,hist,f,enerd,fcd,mtop,top,fr->born,
+ &(top->atomtypes),bBornRadii,box,
+ lambda,graph,&(top->excls),fr->mu_tot,
+ flags,&cycles_pme);
+
+ cycles_force = wallcycle_stop(wcycle,ewcFORCE);
+ GMX_BARRIER(cr->mpi_comm_mygroup);
+
+ if (ed)
+ {
+ do_flood(fplog,cr,x,f,ed,box,step);
+ }
+
+ if (DOMAINDECOMP(cr))
+ {
+ dd_force_flop_stop(cr->dd,nrnb);
+ if (wcycle)
+ {
+ dd_cycles_add(cr->dd,cycles_force-cycles_pme,ddCyclF);
+ }
+ }
+
+ if (bDoForces)
+ {
+ if (IR_ELEC_FIELD(*inputrec))
+ {
+ /* Compute forces due to electric field */
+ calc_f_el(MASTER(cr) ? field : NULL,
+ start,homenr,mdatoms->chargeA,x,fr->f_novirsum,
+ inputrec->ex,inputrec->et,t);
+ }
+
+ /* Communicate the forces */
+ if (PAR(cr))
+ {
+ wallcycle_start(wcycle,ewcMOVEF);
+ if (DOMAINDECOMP(cr))
+ {
+ dd_move_f(cr->dd,f,fr->fshift);
+ /* Do we need to communicate the separate force array
+ * for terms that do not contribute to the single sum virial?
+ * Position restraints and electric fields do not introduce
+ * inter-cg forces, only full electrostatics methods do.
+ * When we do not calculate the virial, fr->f_novirsum = f,
+ * so we have already communicated these forces.
+ */
+ if (EEL_FULL(fr->eeltype) && cr->dd->n_intercg_excl &&
+ (flags & GMX_FORCE_VIRIAL))
+ {
+ dd_move_f(cr->dd,fr->f_novirsum,NULL);
+ }
+ if (bSepLRF)
+ {
+ /* We should not update the shift forces here,
+ * since f_twin is already included in f.
+ */
+ dd_move_f(cr->dd,fr->f_twin,NULL);
+ }
+ }
+ else
+ {
+ pd_move_f(cr,f,nrnb);
+ if (bSepLRF)
+ {
+ pd_move_f(cr,fr->f_twin,nrnb);
+ }
+ }
+ wallcycle_stop(wcycle,ewcMOVEF);
+ }
+
+ /* If we have NoVirSum forces, but we do not calculate the virial,
+ * we sum fr->f_novirum=f later.
+ */
+ if (vsite && !(fr->bF_NoVirSum && !(flags & GMX_FORCE_VIRIAL)))
+ {
+ wallcycle_start(wcycle,ewcVSITESPREAD);
+ spread_vsite_f(fplog,vsite,x,f,fr->fshift,nrnb,
+ &top->idef,fr->ePBC,fr->bMolPBC,graph,box,cr);
+ wallcycle_stop(wcycle,ewcVSITESPREAD);
+
+ if (bSepLRF)
+ {
+ wallcycle_start(wcycle,ewcVSITESPREAD);
+ spread_vsite_f(fplog,vsite,x,fr->f_twin,NULL,
+ nrnb,
+ &top->idef,fr->ePBC,fr->bMolPBC,graph,box,cr);
+ wallcycle_stop(wcycle,ewcVSITESPREAD);
+ }
+ }
+
+ if (flags & GMX_FORCE_VIRIAL)
+ {
+ /* Calculation of the virial must be done after vsites! */
+ calc_virial(fplog,mdatoms->start,mdatoms->homenr,x,f,
+ vir_force,graph,box,nrnb,fr,inputrec->ePBC);
+ }
+ }
+
+ if (inputrec->ePull == epullUMBRELLA || inputrec->ePull == epullCONST_F)
+ {
+ /* Calculate the center of mass forces, this requires communication,
+ * which is why pull_potential is called close to other communication.
+ * The virial contribution is calculated directly,
+ * which is why we call pull_potential after calc_virial.
+ */
+ set_pbc(&pbc,inputrec->ePBC,box);
+ dvdl = 0;
+ enerd->term[F_COM_PULL] =
+ pull_potential(inputrec->ePull,inputrec->pull,mdatoms,&pbc,
+ cr,t,lambda,x,f,vir_force,&dvdl);
+ if (bSepDVDL)
+ {
+ fprintf(fplog,sepdvdlformat,"Com pull",enerd->term[F_COM_PULL],dvdl);
+ }
+ enerd->dvdl_lin += dvdl;
+ }
+ else
+ enerd->term[F_COM_PULL] = 0.0;
+
+ /* Add the forces from enforced rotation potentials (if any) */
+ if (inputrec->bRot)
+ enerd->term[F_COM_PULL] += add_rot_forces(inputrec->rot, f, cr, step, t);
+
+
+ if (PAR(cr) && !(cr->duty & DUTY_PME))
+ {
+ cycles_ppdpme = wallcycle_stop(wcycle,ewcPPDURINGPME);
+ dd_cycles_add(cr->dd,cycles_ppdpme,ddCyclPPduringPME);
+
+ /* In case of node-splitting, the PP nodes receive the long-range
+ * forces, virial and energy from the PME nodes here.
+ */
+ wallcycle_start(wcycle,ewcPP_PMEWAITRECVF);
+ dvdl = 0;
+ gmx_pme_receive_f(cr,fr->f_novirsum,fr->vir_el_recip,&e,&dvdl,
+ &cycles_seppme);
+ if (bSepDVDL)
+ {
+ fprintf(fplog,sepdvdlformat,"PME mesh",e,dvdl);
+ }
+ enerd->term[F_COUL_RECIP] += e;
+ enerd->dvdl_lin += dvdl;
+ if (wcycle)
+ {
+ dd_cycles_add(cr->dd,cycles_seppme,ddCyclPME);
+ }
+ wallcycle_stop(wcycle,ewcPP_PMEWAITRECVF);
+ }
+
+ if (bDoForces && fr->bF_NoVirSum)
+ {
+ if (vsite)
+ {
+ /* Spread the mesh force on virtual sites to the other particles...
+ * This is parallellized. MPI communication is performed
+ * if the constructing atoms aren't local.
+ */
+ wallcycle_start(wcycle,ewcVSITESPREAD);
+ spread_vsite_f(fplog,vsite,x,fr->f_novirsum,NULL,nrnb,
+ &top->idef,fr->ePBC,fr->bMolPBC,graph,box,cr);
+ wallcycle_stop(wcycle,ewcVSITESPREAD);
+ }
+ if (flags & GMX_FORCE_VIRIAL)
+ {
+ /* Now add the forces, this is local */
+ if (fr->bDomDec)
+ {
+ sum_forces(0,fr->f_novirsum_n,f,fr->f_novirsum);
+ }
+ else
+ {
+ sum_forces(start,start+homenr,f,fr->f_novirsum);
+ }
+ if (EEL_FULL(fr->eeltype))
+ {
+ /* Add the mesh contribution to the virial */
+ m_add(vir_force,fr->vir_el_recip,vir_force);
+ }
+ if (debug)
+ {
+ pr_rvecs(debug,0,"vir_force",vir_force,DIM);
+ }
+ }
+ }
+
+ /* Sum the potential energy terms from group contributions */
+ sum_epot(&(inputrec->opts),enerd);
+
+ if (fr->print_force >= 0 && bDoForces)
+ {
+ print_large_forces(stderr,mdatoms,cr,step,fr->print_force,x,f);
+ }
+}
+
+void do_constrain_first(FILE *fplog,gmx_constr_t constr,
+ t_inputrec *ir,t_mdatoms *md,
+ t_state *state,rvec *f,
+ t_graph *graph,t_commrec *cr,t_nrnb *nrnb,
+ t_forcerec *fr, gmx_localtop_t *top, tensor shake_vir)
+{
+ int i,m,start,end;
+ gmx_large_int_t step;
+ double mass,tmass,vcm[4];
+ real dt=ir->delta_t;
+ real dvdlambda;
+ rvec *savex;
+
+ snew(savex,state->natoms);
+
+ start = md->start;
+ end = md->homenr + start;
+
+ if (debug)
+ fprintf(debug,"vcm: start=%d, homenr=%d, end=%d\n",
+ start,md->homenr,end);
+ /* Do a first constrain to reset particles... */
+ step = ir->init_step;
+ if (fplog)
+ {
+ char buf[STEPSTRSIZE];
+ fprintf(fplog,"\nConstraining the starting coordinates (step %s)\n",
+ gmx_step_str(step,buf));
+ }
+ dvdlambda = 0;
+
+ /* constrain the current position */
+ constrain(NULL,TRUE,FALSE,constr,&(top->idef),
+ ir,NULL,cr,step,0,md,
+ state->x,state->x,NULL,
+ state->box,state->lambda,&dvdlambda,
+ NULL,NULL,nrnb,econqCoord,ir->epc==epcMTTK,state->veta,state->veta);
+ if (EI_VV(ir->eI))
+ {
+ /* constrain the inital velocity, and save it */
+ /* also may be useful if we need the ekin from the halfstep for velocity verlet */
+ /* might not yet treat veta correctly */
+ constrain(NULL,TRUE,FALSE,constr,&(top->idef),
+ ir,NULL,cr,step,0,md,
+ state->x,state->v,state->v,
+ state->box,state->lambda,&dvdlambda,
+ NULL,NULL,nrnb,econqVeloc,ir->epc==epcMTTK,state->veta,state->veta);
+ }
+ /* constrain the inital velocities at t-dt/2 */
+ if (EI_STATE_VELOCITY(ir->eI) && ir->eI!=eiVV)
+ {
+ for(i=start; (i<end); i++)
+ {
+ for(m=0; (m<DIM); m++)
+ {
+ /* Reverse the velocity */
+ state->v[i][m] = -state->v[i][m];
+ /* Store the position at t-dt in buf */
+ savex[i][m] = state->x[i][m] + dt*state->v[i][m];
+ }
+ }
+ /* Shake the positions at t=-dt with the positions at t=0
+ * as reference coordinates.
+ */
+ if (fplog)
+ {
+ char buf[STEPSTRSIZE];
+ fprintf(fplog,"\nConstraining the coordinates at t0-dt (step %s)\n",
+ gmx_step_str(step,buf));
+ }
+ dvdlambda = 0;
+ constrain(NULL,TRUE,FALSE,constr,&(top->idef),
+ ir,NULL,cr,step,-1,md,
+ state->x,savex,NULL,
+ state->box,state->lambda,&dvdlambda,
+ state->v,NULL,nrnb,econqCoord,ir->epc==epcMTTK,state->veta,state->veta);
+
+ for(i=start; i<end; i++) {
+ for(m=0; m<DIM; m++) {
+ /* Re-reverse the velocities */
+ state->v[i][m] = -state->v[i][m];
+ }
+ }
+ }
+
+ for(m=0; (m<4); m++)
+ vcm[m] = 0;
+ for(i=start; i<end; i++) {
+ mass = md->massT[i];
+ for(m=0; m<DIM; m++) {
+ vcm[m] += state->v[i][m]*mass;
+ }
+ vcm[3] += mass;
+ }
+
+ if (ir->nstcomm != 0 || debug) {
+ /* Compute the global sum of vcm */
+ if (debug)
+ fprintf(debug,"vcm: %8.3f %8.3f %8.3f,"
+ " total mass = %12.5e\n",vcm[XX],vcm[YY],vcm[ZZ],vcm[3]);
+ if (PAR(cr))
+ gmx_sumd(4,vcm,cr);
+ tmass = vcm[3];
+ for(m=0; (m<DIM); m++)
+ vcm[m] /= tmass;
+ if (debug)
+ fprintf(debug,"vcm: %8.3f %8.3f %8.3f,"
+ " total mass = %12.5e\n",vcm[XX],vcm[YY],vcm[ZZ],tmass);
+ if (ir->nstcomm != 0) {
+ /* Now we have the velocity of center of mass, let's remove it */
+ for(i=start; (i<end); i++) {
+ for(m=0; (m<DIM); m++)
+ state->v[i][m] -= vcm[m];
+ }
+
+ }
+ }
+ sfree(savex);
+}
+
+void calc_enervirdiff(FILE *fplog,int eDispCorr,t_forcerec *fr)
+{
+ double eners[2],virs[2],enersum,virsum,y0,f,g,h;
+ double r0,r1,r,rc3,rc9,ea,eb,ec,pa,pb,pc,pd;
+ double invscale,invscale2,invscale3;
+ int ri0,ri1,ri,i,offstart,offset;
+ real scale,*vdwtab;
+
+ fr->enershiftsix = 0;
+ fr->enershifttwelve = 0;
+ fr->enerdiffsix = 0;
+ fr->enerdifftwelve = 0;
+ fr->virdiffsix = 0;
+ fr->virdifftwelve = 0;
+
+ if (eDispCorr != edispcNO) {
+ for(i=0; i<2; i++) {
+ eners[i] = 0;
+ virs[i] = 0;
+ }
+ if ((fr->vdwtype == evdwSWITCH) || (fr->vdwtype == evdwSHIFT)) {
+ if (fr->rvdw_switch == 0)
+ gmx_fatal(FARGS,
+ "With dispersion correction rvdw-switch can not be zero "
+ "for vdw-type = %s",evdw_names[fr->vdwtype]);
+
+ scale = fr->nblists[0].tab.scale;
+ vdwtab = fr->nblists[0].vdwtab;
+
+ /* Round the cut-offs to exact table values for precision */
+ ri0 = floor(fr->rvdw_switch*scale);
+ ri1 = ceil(fr->rvdw*scale);
+ r0 = ri0/scale;
+ r1 = ri1/scale;
+ rc3 = r0*r0*r0;
+ rc9 = rc3*rc3*rc3;
+
+ if (fr->vdwtype == evdwSHIFT) {
+ /* Determine the constant energy shift below rvdw_switch */
+ fr->enershiftsix = (real)(-1.0/(rc3*rc3)) - vdwtab[8*ri0];
+ fr->enershifttwelve = (real)( 1.0/(rc9*rc3)) - vdwtab[8*ri0 + 4];
+ }
+ /* Add the constant part from 0 to rvdw_switch.
+ * This integration from 0 to rvdw_switch overcounts the number
+ * of interactions by 1, as it also counts the self interaction.
+ * We will correct for this later.
+ */
+ eners[0] += 4.0*M_PI*fr->enershiftsix*rc3/3.0;
+ eners[1] += 4.0*M_PI*fr->enershifttwelve*rc3/3.0;
+
+ invscale = 1.0/(scale);
+ invscale2 = invscale*invscale;
+ invscale3 = invscale*invscale2;
+
+ /* following summation derived from cubic spline definition,
+ Numerical Recipies in C, second edition, p. 113-116. Exact
+ for the cubic spline. We first calculate the negative of
+ the energy from rvdw to rvdw_switch, assuming that g(r)=1,
+ and then add the more standard, abrupt cutoff correction to
+ that result, yielding the long-range correction for a
+ switched function. We perform both the pressure and energy
+ loops at the same time for simplicity, as the computational
+ cost is low. */
+
+ for (i=0;i<2;i++) {
+ enersum = 0.0; virsum = 0.0;
+ if (i==0)
+ offstart = 0;
+ else
+ offstart = 4;
+ for (ri=ri0; ri<ri1; ri++) {
+ r = ri*invscale;
+ ea = invscale3;
+ eb = 2.0*invscale2*r;
+ ec = invscale*r*r;
+
+ pa = invscale3;
+ pb = 3.0*invscale2*r;
+ pc = 3.0*invscale*r*r;
+ pd = r*r*r;
+
+ /* this "8" is from the packing in the vdwtab array - perhaps
+ should be #define'ed? */
+ offset = 8*ri + offstart;
+ y0 = vdwtab[offset];
+ f = vdwtab[offset+1];
+ g = vdwtab[offset+2];
+ h = vdwtab[offset+3];
+
+ enersum += y0*(ea/3 + eb/2 + ec) + f*(ea/4 + eb/3 + ec/2)+
+ g*(ea/5 + eb/4 + ec/3) + h*(ea/6 + eb/5 + ec/4);
+ virsum += f*(pa/4 + pb/3 + pc/2 + pd) +
+ 2*g*(pa/5 + pb/4 + pc/3 + pd/2) + 3*h*(pa/6 + pb/5 + pc/4 + pd/3);
+
+ }
+ enersum *= 4.0*M_PI;
+ virsum *= 4.0*M_PI;
+ eners[i] -= enersum;
+ virs[i] -= virsum;
+ }
+
+ /* now add the correction for rvdw_switch to infinity */
+ eners[0] += -4.0*M_PI/(3.0*rc3);
+ eners[1] += 4.0*M_PI/(9.0*rc9);
+ virs[0] += 8.0*M_PI/rc3;
+ virs[1] += -16.0*M_PI/(3.0*rc9);
+ }
+ else if ((fr->vdwtype == evdwCUT) || (fr->vdwtype == evdwUSER)) {
+ if (fr->vdwtype == evdwUSER && fplog)
+ fprintf(fplog,
+ "WARNING: using dispersion correction with user tables\n");
+ rc3 = fr->rvdw*fr->rvdw*fr->rvdw;
+ rc9 = rc3*rc3*rc3;
+ eners[0] += -4.0*M_PI/(3.0*rc3);
+ eners[1] += 4.0*M_PI/(9.0*rc9);
+ virs[0] += 8.0*M_PI/rc3;
+ virs[1] += -16.0*M_PI/(3.0*rc9);
+ } else {
+ gmx_fatal(FARGS,
+ "Dispersion correction is not implemented for vdw-type = %s",
+ evdw_names[fr->vdwtype]);
+ }
+ fr->enerdiffsix = eners[0];
+ fr->enerdifftwelve = eners[1];
+ /* The 0.5 is due to the Gromacs definition of the virial */
+ fr->virdiffsix = 0.5*virs[0];
+ fr->virdifftwelve = 0.5*virs[1];
+ }
+}
+
+void calc_dispcorr(FILE *fplog,t_inputrec *ir,t_forcerec *fr,
+ gmx_large_int_t step,int natoms,
+ matrix box,real lambda,tensor pres,tensor virial,
+ real *prescorr, real *enercorr, real *dvdlcorr)
+{
+ gmx_bool bCorrAll,bCorrPres;
+ real dvdlambda,invvol,dens,ninter,avcsix,avctwelve,enerdiff,svir=0,spres=0;
+ int m;
+
+ *prescorr = 0;
+ *enercorr = 0;
+ *dvdlcorr = 0;
+
+ clear_mat(virial);
+ clear_mat(pres);
+
+ if (ir->eDispCorr != edispcNO) {
+ bCorrAll = (ir->eDispCorr == edispcAllEner ||
+ ir->eDispCorr == edispcAllEnerPres);
+ bCorrPres = (ir->eDispCorr == edispcEnerPres ||
+ ir->eDispCorr == edispcAllEnerPres);
+
+ invvol = 1/det(box);
+ if (fr->n_tpi)
+ {
+ /* Only correct for the interactions with the inserted molecule */
+ dens = (natoms - fr->n_tpi)*invvol;
+ ninter = fr->n_tpi;
+ }
+ else
+ {
+ dens = natoms*invvol;
+ ninter = 0.5*natoms;
+ }
+
+ if (ir->efep == efepNO)
+ {
+ avcsix = fr->avcsix[0];
+ avctwelve = fr->avctwelve[0];
+ }
+ else
+ {
+ avcsix = (1 - lambda)*fr->avcsix[0] + lambda*fr->avcsix[1];
+ avctwelve = (1 - lambda)*fr->avctwelve[0] + lambda*fr->avctwelve[1];
+ }
+
+ enerdiff = ninter*(dens*fr->enerdiffsix - fr->enershiftsix);
+ *enercorr += avcsix*enerdiff;
+ dvdlambda = 0.0;
+ if (ir->efep != efepNO)
+ {
+ dvdlambda += (fr->avcsix[1] - fr->avcsix[0])*enerdiff;
+ }
+ if (bCorrAll)
+ {
+ enerdiff = ninter*(dens*fr->enerdifftwelve - fr->enershifttwelve);
+ *enercorr += avctwelve*enerdiff;
+ if (fr->efep != efepNO)
+ {
+ dvdlambda += (fr->avctwelve[1] - fr->avctwelve[0])*enerdiff;
+ }
+ }
+
+ if (bCorrPres)
+ {
+ svir = ninter*dens*avcsix*fr->virdiffsix/3.0;
+ if (ir->eDispCorr == edispcAllEnerPres)
+ {
+ svir += ninter*dens*avctwelve*fr->virdifftwelve/3.0;
+ }
+ /* The factor 2 is because of the Gromacs virial definition */
+ spres = -2.0*invvol*svir*PRESFAC;
+
+ for(m=0; m<DIM; m++) {
+ virial[m][m] += svir;
+ pres[m][m] += spres;
+ }
+ *prescorr += spres;
+ }
+
+ /* Can't currently control when it prints, for now, just print when degugging */
+ if (debug)
+ {
+ if (bCorrAll) {
+ fprintf(debug,"Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
+ avcsix,avctwelve);
+ }
+ if (bCorrPres)
+ {
+ fprintf(debug,
+ "Long Range LJ corr.: Epot %10g, Pres: %10g, Vir: %10g\n",
+ *enercorr,spres,svir);
+ }
+ else
+ {
+ fprintf(debug,"Long Range LJ corr.: Epot %10g\n",*enercorr);
+ }
+ }
+
+ if (fr->bSepDVDL && do_per_step(step,ir->nstlog))
+ {
+ fprintf(fplog,sepdvdlformat,"Dispersion correction",
+ *enercorr,dvdlambda);
+ }
+ if (fr->efep != efepNO)
+ {
+ *dvdlcorr += dvdlambda;
+ }
+ }
+}
+
+void do_pbc_first(FILE *fplog,matrix box,t_forcerec *fr,
+ t_graph *graph,rvec x[])
+{
+ if (fplog)
+ fprintf(fplog,"Removing pbc first time\n");
+ calc_shifts(box,fr->shift_vec);
+ if (graph) {
+ mk_mshift(fplog,graph,fr->ePBC,box,x);
+ if (gmx_debug_at)
+ p_graph(debug,"do_pbc_first 1",graph);
+ shift_self(graph,box,x);
+ /* By doing an extra mk_mshift the molecules that are broken
+ * because they were e.g. imported from another software
+ * will be made whole again. Such are the healing powers
+ * of GROMACS.
+ */
+ mk_mshift(fplog,graph,fr->ePBC,box,x);
+ if (gmx_debug_at)
+ p_graph(debug,"do_pbc_first 2",graph);
+ }
+ if (fplog)
+ fprintf(fplog,"Done rmpbc\n");
+}
+
+static void low_do_pbc_mtop(FILE *fplog,int ePBC,matrix box,
+ gmx_mtop_t *mtop,rvec x[],
+ gmx_bool bFirst)
+{
+ t_graph *graph;
+ int mb,as,mol;
+ gmx_molblock_t *molb;
+
+ if (bFirst && fplog)
+ fprintf(fplog,"Removing pbc first time\n");
+
+ snew(graph,1);
+ as = 0;
+ for(mb=0; mb<mtop->nmolblock; mb++) {
+ molb = &mtop->molblock[mb];
+ if (molb->natoms_mol == 1 ||
+ (!bFirst && mtop->moltype[molb->type].cgs.nr == 1)) {
+ /* Just one atom or charge group in the molecule, no PBC required */
+ as += molb->nmol*molb->natoms_mol;
+ } else {
+ /* Pass NULL iso fplog to avoid graph prints for each molecule type */
+ mk_graph_ilist(NULL,mtop->moltype[molb->type].ilist,
+ 0,molb->natoms_mol,FALSE,FALSE,graph);
+
+ for(mol=0; mol<molb->nmol; mol++) {
+ mk_mshift(fplog,graph,ePBC,box,x+as);
+
+ shift_self(graph,box,x+as);
+ /* The molecule is whole now.
+ * We don't need the second mk_mshift call as in do_pbc_first,
+ * since we no longer need this graph.
+ */
+
+ as += molb->natoms_mol;
+ }
+ done_graph(graph);
+ }
+ }
+ sfree(graph);
+}
+
+void do_pbc_first_mtop(FILE *fplog,int ePBC,matrix box,
+ gmx_mtop_t *mtop,rvec x[])
+{
+ low_do_pbc_mtop(fplog,ePBC,box,mtop,x,TRUE);
+}
+
+void do_pbc_mtop(FILE *fplog,int ePBC,matrix box,
+ gmx_mtop_t *mtop,rvec x[])
+{
+ low_do_pbc_mtop(fplog,ePBC,box,mtop,x,FALSE);
+}
+
+void finish_run(FILE *fplog,t_commrec *cr,const char *confout,
+ t_inputrec *inputrec,
+ t_nrnb nrnb[],gmx_wallcycle_t wcycle,
+ gmx_runtime_t *runtime,
+ gmx_bool bWriteStat)
+{
+ int i,j;
+ t_nrnb *nrnb_tot=NULL;
+ real delta_t;
+ double nbfs,mflop;
+ double cycles[ewcNR];
+
+ wallcycle_sum(cr,wcycle,cycles);
+
+ if (cr->nnodes > 1) {
+ if (SIMMASTER(cr))
+ snew(nrnb_tot,1);
+#ifdef GMX_MPI
+ MPI_Reduce(nrnb->n,nrnb_tot->n,eNRNB,MPI_DOUBLE,MPI_SUM,
+ MASTERRANK(cr),cr->mpi_comm_mysim);
+#endif
+ } else {
+ nrnb_tot = nrnb;
+ }
+
+ if (SIMMASTER(cr)) {
+ print_flop(fplog,nrnb_tot,&nbfs,&mflop);
+ if (cr->nnodes > 1) {
+ sfree(nrnb_tot);
+ }
+ }
+
+ if ((cr->duty & DUTY_PP) && DOMAINDECOMP(cr)) {
+ print_dd_statistics(cr,inputrec,fplog);
+ }
+
+#ifdef GMX_MPI
+ if (PARTDECOMP(cr))
+ {
+ if (MASTER(cr))
+ {
+ t_nrnb *nrnb_all;
+ int s;
+ MPI_Status stat;
+
+ snew(nrnb_all,cr->nnodes);
+ nrnb_all[0] = *nrnb;
+ for(s=1; s<cr->nnodes; s++)
+ {
+ MPI_Recv(nrnb_all[s].n,eNRNB,MPI_DOUBLE,s,0,
+ cr->mpi_comm_mysim,&stat);
+ }
+ pr_load(fplog,cr,nrnb_all);
+ sfree(nrnb_all);
+ }
+ else
+ {
+ MPI_Send(nrnb->n,eNRNB,MPI_DOUBLE,MASTERRANK(cr),0,
+ cr->mpi_comm_mysim);
+ }
+ }
+#endif
+
+ if (SIMMASTER(cr)) {
+ wallcycle_print(fplog,cr->nnodes,cr->npmenodes,runtime->realtime,
+ wcycle,cycles);
+
+ if (EI_DYNAMICS(inputrec->eI)) {
+ delta_t = inputrec->delta_t;
+ } else {
+ delta_t = 0;
+ }
+
+ if (fplog) {
+ print_perf(fplog,runtime->proctime,runtime->realtime,
+ cr->nnodes-cr->npmenodes,
+ runtime->nsteps_done,delta_t,nbfs,mflop);
+ }
+ if (bWriteStat) {
+ print_perf(stderr,runtime->proctime,runtime->realtime,
+ cr->nnodes-cr->npmenodes,
+ runtime->nsteps_done,delta_t,nbfs,mflop);
+ }
+
+ /*
+ runtime=inputrec->nsteps*inputrec->delta_t;
+ if (bWriteStat) {
+ if (cr->nnodes == 1)
+ fprintf(stderr,"\n\n");
+ print_perf(stderr,nodetime,realtime,runtime,&ntot,
+ cr->nnodes-cr->npmenodes,FALSE);
+ }
+ wallcycle_print(fplog,cr->nnodes,cr->npmenodes,realtime,wcycle,cycles);
+ print_perf(fplog,nodetime,realtime,runtime,&ntot,cr->nnodes-cr->npmenodes,
+ TRUE);
+ if (PARTDECOMP(cr))
+ pr_load(fplog,cr,nrnb_all);
+ if (cr->nnodes > 1)
+ sfree(nrnb_all);
+ */
+ }
+}
+
+void init_md(FILE *fplog,
+ t_commrec *cr,t_inputrec *ir,const output_env_t oenv,
+ double *t,double *t0,
+ real *lambda,double *lam0,
+ t_nrnb *nrnb,gmx_mtop_t *mtop,
+ gmx_update_t *upd,
+ int nfile,const t_filenm fnm[],
+ gmx_mdoutf_t **outf,t_mdebin **mdebin,
+ tensor force_vir,tensor shake_vir,rvec mu_tot,
+ gmx_bool *bSimAnn,t_vcm **vcm, t_state *state, unsigned long Flags)
+{
+ int i,j,n;
+ real tmpt,mod;
+
+ /* Initial values */
+ *t = *t0 = ir->init_t;
+ if (ir->efep != efepNO)
+ {
+ *lam0 = ir->init_lambda;
+ *lambda = *lam0 + ir->init_step*ir->delta_lambda;
+ }
+ else
+ {
+ *lambda = *lam0 = 0.0;
+ }
+
+ *bSimAnn=FALSE;
+ for(i=0;i<ir->opts.ngtc;i++)
+ {
+ /* set bSimAnn if any group is being annealed */
+ if(ir->opts.annealing[i]!=eannNO)
+ {
+ *bSimAnn = TRUE;
+ }
+ }
+ if (*bSimAnn)
+ {
+ update_annealing_target_temp(&(ir->opts),ir->init_t);
+ }
+
+ if (upd)
+ {
+ *upd = init_update(fplog,ir);
+ }
+
+ if (vcm != NULL)
+ {
+ *vcm = init_vcm(fplog,&mtop->groups,ir);
+ }
+
+ if (EI_DYNAMICS(ir->eI) && !(Flags & MD_APPENDFILES))
+ {
+ if (ir->etc == etcBERENDSEN)
+ {
+ please_cite(fplog,"Berendsen84a");
+ }
+ if (ir->etc == etcVRESCALE)
+ {
+ please_cite(fplog,"Bussi2007a");
+ }
+ }
+
+ init_nrnb(nrnb);
+
+ if (nfile != -1)
+ {
+ *outf = init_mdoutf(nfile,fnm,Flags,cr,ir,oenv);
+
+ *mdebin = init_mdebin((Flags & MD_APPENDFILES) ? NULL : (*outf)->fp_ene,
+ mtop,ir, (*outf)->fp_dhdl);
+ }
+
+ /* Initiate variables */
+ clear_mat(force_vir);
+ clear_mat(shake_vir);
+ clear_rvec(mu_tot);
+
+ debug_gmx();
+}
+
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include "sysstuff.h"
+#include "string2.h"
+#include "network.h"
+#include "confio.h"
+#include "copyrite.h"
+#include "smalloc.h"
+#include "nrnb.h"
+#include "main.h"
+#include "chargegroup.h"
+#include "force.h"
+#include "macros.h"
+#include "random.h"
+#include "names.h"
+#include "gmx_fatal.h"
+#include "txtdump.h"
+#include "typedefs.h"
+#include "update.h"
+#include "random.h"
+#include "constr.h"
+#include "vec.h"
+#include "statutil.h"
+#include "tgroup.h"
+#include "mdebin.h"
+#include "vsite.h"
+#include "force.h"
+#include "mdrun.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "gmx_random.h"
+#include "physics.h"
+#include "xvgr.h"
+#include "mdatoms.h"
+#include "ns.h"
+#include "gmx_wallcycle.h"
+#include "mtop_util.h"
+#include "gmxfio.h"
+#include "pme.h"
+#include "gbutil.h"
+
+#if ( defined(GMX_IA32_SSE) || defined(GMX_X86_64_SSE) || defined(GMX_X86_64_SSE2) )
+#if defined(GMX_DOUBLE)
+#include "gmx_sse2_double.h"
+#else
+#include "gmx_sse2_single.h"
+#endif
+#endif
+
+
+static void global_max(t_commrec *cr,int *n)
+{
+ int *sum,i;
+
+ snew(sum,cr->nnodes);
+ sum[cr->nodeid] = *n;
+ gmx_sumi(cr->nnodes,sum,cr);
+ for(i=0; i<cr->nnodes; i++)
+ *n = max(*n,sum[i]);
+
+ sfree(sum);
+}
+
+static void realloc_bins(double **bin,int *nbin,int nbin_new)
+{
+ int i;
+
+ if (nbin_new != *nbin) {
+ srenew(*bin,nbin_new);
+ for(i=*nbin; i<nbin_new; i++)
+ (*bin)[i] = 0;
+ *nbin = nbin_new;
+ }
+}
+
+double do_tpi(FILE *fplog,t_commrec *cr,
+ int nfile, const t_filenm fnm[],
+ const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
+ int nstglobalcomm,
+ gmx_vsite_t *vsite,gmx_constr_t constr,
+ int stepout,
+ t_inputrec *inputrec,
+ gmx_mtop_t *top_global,t_fcdata *fcd,
+ t_state *state,
+ t_mdatoms *mdatoms,
+ t_nrnb *nrnb,gmx_wallcycle_t wcycle,
+ gmx_edsam_t ed,
+ t_forcerec *fr,
+ int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ const char *TPI="Test Particle Insertion";
+ gmx_localtop_t *top;
+ gmx_groups_t *groups;
+ gmx_enerdata_t *enerd;
+ rvec *f;
+ real lambda,t,temp,beta,drmax,epot;
+ double embU,sum_embU,*sum_UgembU,V,V_all,VembU_all;
+ t_trxstatus *status;
+ t_trxframe rerun_fr;
+ gmx_bool bDispCorr,bCharge,bRFExcl,bNotLastFrame,bStateChanged,bNS,bOurStep;
+ tensor force_vir,shake_vir,vir,pres;
+ int cg_tp,a_tp0,a_tp1,ngid,gid_tp,nener,e;
+ rvec *x_mol;
+ rvec mu_tot,x_init,dx,x_tp;
+ int nnodes,frame,nsteps,step;
+ int i,start,end;
+ gmx_rng_t tpi_rand;
+ FILE *fp_tpi=NULL;
+ char *ptr,*dump_pdb,**leg,str[STRLEN],str2[STRLEN];
+ double dbl,dump_ener;
+ gmx_bool bCavity;
+ int nat_cavity=0,d;
+ real *mass_cavity=NULL,mass_tot;
+ int nbin;
+ double invbinw,*bin,refvolshift,logV,bUlogV;
+ real dvdl,prescorr,enercorr,dvdlcorr;
+ gmx_bool bEnergyOutOfBounds;
+ const char *tpid_leg[2]={"direct","reweighted"};
+
+ /* Since there is no upper limit to the insertion energies,
+ * we need to set an upper limit for the distribution output.
+ */
+ real bU_bin_limit = 50;
+ real bU_logV_bin_limit = bU_bin_limit + 10;
+
+ nnodes = cr->nnodes;
+
+ top = gmx_mtop_generate_local_top(top_global,inputrec);
+
+ groups = &top_global->groups;
+
+ bCavity = (inputrec->eI == eiTPIC);
+ if (bCavity) {
+ ptr = getenv("GMX_TPIC_MASSES");
+ if (ptr == NULL) {
+ nat_cavity = 1;
+ } else {
+ /* Read (multiple) masses from env var GMX_TPIC_MASSES,
+ * The center of mass of the last atoms is then used for TPIC.
+ */
+ nat_cavity = 0;
+ while (sscanf(ptr,"%lf%n",&dbl,&i) > 0) {
+ srenew(mass_cavity,nat_cavity+1);
+ mass_cavity[nat_cavity] = dbl;
+ fprintf(fplog,"mass[%d] = %f\n",
+ nat_cavity+1,mass_cavity[nat_cavity]);
+ nat_cavity++;
+ ptr += i;
+ }
+ if (nat_cavity == 0)
+ gmx_fatal(FARGS,"Found %d masses in GMX_TPIC_MASSES",nat_cavity);
+ }
+ }
+
+ /*
+ init_em(fplog,TPI,inputrec,&lambda,nrnb,mu_tot,
+ state->box,fr,mdatoms,top,cr,nfile,fnm,NULL,NULL);*/
+ /* We never need full pbc for TPI */
+ fr->ePBC = epbcXYZ;
+ /* Determine the temperature for the Boltzmann weighting */
+ temp = inputrec->opts.ref_t[0];
+ if (fplog) {
+ for(i=1; (i<inputrec->opts.ngtc); i++) {
+ if (inputrec->opts.ref_t[i] != temp) {
+ fprintf(fplog,"\nWARNING: The temperatures of the different temperature coupling groups are not identical\n\n");
+ fprintf(stderr,"\nWARNING: The temperatures of the different temperature coupling groups are not identical\n\n");
+ }
+ }
+ fprintf(fplog,
+ "\n The temperature for test particle insertion is %.3f K\n\n",
+ temp);
+ }
+ beta = 1.0/(BOLTZ*temp);
+
+ /* Number of insertions per frame */
+ nsteps = inputrec->nsteps;
+
+ /* Use the same neighborlist with more insertions points
+ * in a sphere of radius drmax around the initial point
+ */
+ /* This should be a proper mdp parameter */
+ drmax = inputrec->rtpi;
+
+ /* An environment variable can be set to dump all configurations
+ * to pdb with an insertion energy <= this value.
+ */
+ dump_pdb = getenv("GMX_TPI_DUMP");
+ dump_ener = 0;
+ if (dump_pdb)
+ sscanf(dump_pdb,"%lf",&dump_ener);
+
+ atoms2md(top_global,inputrec,0,NULL,0,top_global->natoms,mdatoms);
+ update_mdatoms(mdatoms,inputrec->init_lambda);
+
+ snew(enerd,1);
+ init_enerdata(groups->grps[egcENER].nr,inputrec->n_flambda,enerd);
+ snew(f,top_global->natoms);
+
+ /* Print to log file */
+ runtime_start(runtime);
+ print_date_and_time(fplog,cr->nodeid,
+ "Started Test Particle Insertion",runtime);
+ wallcycle_start(wcycle,ewcRUN);
+
+ /* The last charge group is the group to be inserted */
+ cg_tp = top->cgs.nr - 1;
+ a_tp0 = top->cgs.index[cg_tp];
+ a_tp1 = top->cgs.index[cg_tp+1];
+ if (debug)
+ fprintf(debug,"TPI cg %d, atoms %d-%d\n",cg_tp,a_tp0,a_tp1);
+ if (a_tp1 - a_tp0 > 1 &&
+ (inputrec->rlist < inputrec->rcoulomb ||
+ inputrec->rlist < inputrec->rvdw))
+ gmx_fatal(FARGS,"Can not do TPI for multi-atom molecule with a twin-range cut-off");
+ snew(x_mol,a_tp1-a_tp0);
+
+ bDispCorr = (inputrec->eDispCorr != edispcNO);
+ bCharge = FALSE;
+ for(i=a_tp0; i<a_tp1; i++) {
+ /* Copy the coordinates of the molecule to be insterted */
+ copy_rvec(state->x[i],x_mol[i-a_tp0]);
+ /* Check if we need to print electrostatic energies */
+ bCharge |= (mdatoms->chargeA[i] != 0 ||
+ (mdatoms->chargeB && mdatoms->chargeB[i] != 0));
+ }
+ bRFExcl = (bCharge && EEL_RF(fr->eeltype) && fr->eeltype!=eelRF_NEC);
+
+ calc_cgcm(fplog,cg_tp,cg_tp+1,&(top->cgs),state->x,fr->cg_cm);
+ if (bCavity) {
+ if (norm(fr->cg_cm[cg_tp]) > 0.5*inputrec->rlist && fplog) {
+ fprintf(fplog, "WARNING: Your TPI molecule is not centered at 0,0,0\n");
+ fprintf(stderr,"WARNING: Your TPI molecule is not centered at 0,0,0\n");
+ }
+ } else {
+ /* Center the molecule to be inserted at zero */
+ for(i=0; i<a_tp1-a_tp0; i++)
+ rvec_dec(x_mol[i],fr->cg_cm[cg_tp]);
+ }
+
+ if (fplog) {
+ fprintf(fplog,"\nWill insert %d atoms %s partial charges\n",
+ a_tp1-a_tp0,bCharge ? "with" : "without");
+
+ fprintf(fplog,"\nWill insert %d times in each frame of %s\n",
+ nsteps,opt2fn("-rerun",nfile,fnm));
+ }
+
+ if (!bCavity)
+ {
+ if (inputrec->nstlist > 1)
+ {
+ if (drmax==0 && a_tp1-a_tp0==1)
+ {
+ gmx_fatal(FARGS,"Re-using the neighborlist %d times for insertions of a single atom in a sphere of radius %f does not make sense",inputrec->nstlist,drmax);
+ }
+ if (fplog)
+ {
+ fprintf(fplog,"Will use the same neighborlist for %d insertions in a sphere of radius %f\n",inputrec->nstlist,drmax);
+ }
+ }
+ }
+ else
+ {
+ if (fplog)
+ {
+ fprintf(fplog,"Will insert randomly in a sphere of radius %f around the center of the cavity\n",drmax);
+ }
+ }
+
+ ngid = groups->grps[egcENER].nr;
+ gid_tp = GET_CGINFO_GID(fr->cginfo[cg_tp]);
+ nener = 1 + ngid;
+ if (bDispCorr)
+ nener += 1;
+ if (bCharge) {
+ nener += ngid;
+ if (bRFExcl)
+ nener += 1;
+ if (EEL_FULL(fr->eeltype))
+ nener += 1;
+ }
+ snew(sum_UgembU,nener);
+
+ /* Initialize random generator */
+ tpi_rand = gmx_rng_init(inputrec->ld_seed);
+
+ if (MASTER(cr)) {
+ fp_tpi = xvgropen(opt2fn("-tpi",nfile,fnm),
+ "TPI energies","Time (ps)",
+ "(kJ mol\\S-1\\N) / (nm\\S3\\N)",oenv);
+ xvgr_subtitle(fp_tpi,"f. are averages over one frame",oenv);
+ snew(leg,4+nener);
+ e = 0;
+ sprintf(str,"-kT log(<Ve\\S-\\betaU\\N>/<V>)");
+ leg[e++] = strdup(str);
+ sprintf(str,"f. -kT log<e\\S-\\betaU\\N>");
+ leg[e++] = strdup(str);
+ sprintf(str,"f. <e\\S-\\betaU\\N>");
+ leg[e++] = strdup(str);
+ sprintf(str,"f. V");
+ leg[e++] = strdup(str);
+ sprintf(str,"f. <Ue\\S-\\betaU\\N>");
+ leg[e++] = 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);
+ }
+ if (bDispCorr) {
+ sprintf(str,"f. <U\\sdisp c\\Ne\\S-\\betaU\\N>");
+ leg[e++] = strdup(str);
+ }
+ if (bCharge) {
+ for(i=0; i<ngid; i++) {
+ sprintf(str,"f. <U\\sCoul %s\\Ne\\S-\\betaU\\N>",
+ *(groups->grpname[groups->grps[egcENER].nm_ind[i]]));
+ leg[e++] = strdup(str);
+ }
+ if (bRFExcl) {
+ sprintf(str,"f. <U\\sRF excl\\Ne\\S-\\betaU\\N>");
+ leg[e++] = strdup(str);
+ }
+ if (EEL_FULL(fr->eeltype)) {
+ sprintf(str,"f. <U\\sCoul recip\\Ne\\S-\\betaU\\N>");
+ leg[e++] = strdup(str);
+ }
+ }
+ xvgr_legend(fp_tpi,4+nener,(const char**)leg,oenv);
+ for(i=0; i<4+nener; i++)
+ sfree(leg[i]);
+ sfree(leg);
+ }
+ clear_rvec(x_init);
+ V_all = 0;
+ VembU_all = 0;
+
+ invbinw = 10;
+ nbin = 10;
+ snew(bin,nbin);
+
+ bNotLastFrame = read_first_frame(oenv,&status,opt2fn("-rerun",nfile,fnm),
+ &rerun_fr,TRX_NEED_X);
+ frame = 0;
+
+ if (rerun_fr.natoms - (bCavity ? nat_cavity : 0) !=
+ mdatoms->nr - (a_tp1 - a_tp0))
+ gmx_fatal(FARGS,"Number of atoms in trajectory (%d)%s "
+ "is not equal the number in the run input file (%d) "
+ "minus the number of atoms to insert (%d)\n",
+ rerun_fr.natoms,bCavity ? " minus one" : "",
+ mdatoms->nr,a_tp1-a_tp0);
+
+ refvolshift = log(det(rerun_fr.box));
+
+#if ( defined(GMX_IA32_SSE) || defined(GMX_X86_64_SSE) || defined(GMX_X86_64_SSE2) )
+ /* Make sure we don't detect SSE overflow generated before this point */
+ gmx_mm_check_and_reset_overflow();
+#endif
+
+ while (bNotLastFrame)
+ {
+ lambda = rerun_fr.lambda;
+ t = rerun_fr.time;
+
+ sum_embU = 0;
+ for(e=0; e<nener; e++)
+ {
+ sum_UgembU[e] = 0;
+ }
+
+ /* Copy the coordinates from the input trajectory */
+ for(i=0; i<rerun_fr.natoms; i++)
+ {
+ copy_rvec(rerun_fr.x[i],state->x[i]);
+ }
+
+ V = det(rerun_fr.box);
+ logV = log(V);
+
+ bStateChanged = TRUE;
+ bNS = TRUE;
+ for(step=0; step<nsteps; step++)
+ {
+ /* In parallel all nodes generate all random configurations.
+ * In that way the result is identical to a single cpu tpi run.
+ */
+ if (!bCavity)
+ {
+ /* Random insertion in the whole volume */
+ bNS = (step % inputrec->nstlist == 0);
+ if (bNS)
+ {
+ /* Generate a random position in the box */
+ x_init[XX] = gmx_rng_uniform_real(tpi_rand)*state->box[XX][XX];
+ x_init[YY] = gmx_rng_uniform_real(tpi_rand)*state->box[YY][YY];
+ x_init[ZZ] = gmx_rng_uniform_real(tpi_rand)*state->box[ZZ][ZZ];
+ }
+ if (inputrec->nstlist == 1)
+ {
+ copy_rvec(x_init,x_tp);
+ }
+ else
+ {
+ /* Generate coordinates within |dx|=drmax of x_init */
+ do
+ {
+ dx[XX] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
+ dx[YY] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
+ dx[ZZ] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
+ }
+ while (norm2(dx) > drmax*drmax);
+ rvec_add(x_init,dx,x_tp);
+ }
+ }
+ else
+ {
+ /* Random insertion around a cavity location
+ * given by the last coordinate of the trajectory.
+ */
+ if (step == 0)
+ {
+ if (nat_cavity == 1)
+ {
+ /* Copy the location of the cavity */
+ copy_rvec(rerun_fr.x[rerun_fr.natoms-1],x_init);
+ }
+ else
+ {
+ /* Determine the center of mass of the last molecule */
+ clear_rvec(x_init);
+ mass_tot = 0;
+ for(i=0; i<nat_cavity; i++)
+ {
+ for(d=0; d<DIM; d++)
+ {
+ x_init[d] +=
+ mass_cavity[i]*rerun_fr.x[rerun_fr.natoms-nat_cavity+i][d];
+ }
+ mass_tot += mass_cavity[i];
+ }
+ for(d=0; d<DIM; d++)
+ {
+ x_init[d] /= mass_tot;
+ }
+ }
+ }
+ /* Generate coordinates within |dx|=drmax of x_init */
+ do
+ {
+ dx[XX] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
+ dx[YY] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
+ dx[ZZ] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
+ }
+ while (norm2(dx) > drmax*drmax);
+ rvec_add(x_init,dx,x_tp);
+ }
+
+ if (a_tp1 - a_tp0 == 1)
+ {
+ /* Insert a single atom, just copy the insertion location */
+ copy_rvec(x_tp,state->x[a_tp0]);
+ }
+ else
+ {
+ /* Copy the coordinates from the top file */
+ for(i=a_tp0; i<a_tp1; i++)
+ {
+ copy_rvec(x_mol[i-a_tp0],state->x[i]);
+ }
+ /* Rotate the molecule randomly */
+ rotate_conf(a_tp1-a_tp0,state->x+a_tp0,NULL,
+ 2*M_PI*gmx_rng_uniform_real(tpi_rand),
+ 2*M_PI*gmx_rng_uniform_real(tpi_rand),
+ 2*M_PI*gmx_rng_uniform_real(tpi_rand));
+ /* Shift to the insertion location */
+ for(i=a_tp0; i<a_tp1; i++)
+ {
+ rvec_inc(state->x[i],x_tp);
+ }
+ }
+
+ /* Check if this insertion belongs to this node */
+ bOurStep = TRUE;
+ if (PAR(cr))
+ {
+ switch (inputrec->eI)
+ {
+ case eiTPI:
+ bOurStep = ((step / inputrec->nstlist) % nnodes == cr->nodeid);
+ break;
+ case eiTPIC:
+ bOurStep = (step % nnodes == cr->nodeid);
+ break;
+ default:
+ gmx_fatal(FARGS,"Unknown integrator %s",ei_names[inputrec->eI]);
+ }
+ }
+ if (bOurStep)
+ {
+ /* Clear some matrix variables */
+ clear_mat(force_vir);
+ clear_mat(shake_vir);
+ clear_mat(vir);
+ clear_mat(pres);
+
+ /* Set the charge group center of mass of the test particle */
+ copy_rvec(x_init,fr->cg_cm[top->cgs.nr-1]);
+
+ /* Calc energy (no forces) on new positions.
+ * Since we only need the intermolecular energy
+ * and the RF exclusion terms of the inserted molecule occur
+ * within a single charge group we can pass NULL for the graph.
+ * This also avoids shifts that would move charge groups
+ * out of the box.
+ *
+ * Some checks above ensure than we can not have
+ * twin-range interactions together with nstlist > 1,
+ * therefore we do not need to remember the LR energies.
+ */
+ /* Make do_force do a single node force calculation */
+ cr->nnodes = 1;
+ do_force(fplog,cr,inputrec,
+ step,nrnb,wcycle,top,top_global,&top_global->groups,
+ rerun_fr.box,state->x,&state->hist,
+ f,force_vir,mdatoms,enerd,fcd,
+ lambda,NULL,fr,NULL,mu_tot,t,NULL,NULL,FALSE,
+ GMX_FORCE_NONBONDED |
+ (bNS ? GMX_FORCE_NS | GMX_FORCE_DOLR : 0) |
+ (bStateChanged ? GMX_FORCE_STATECHANGED : 0));
+ cr->nnodes = nnodes;
+ bStateChanged = FALSE;
+ bNS = FALSE;
+
+ /* Calculate long range corrections to pressure and energy */
+ calc_dispcorr(fplog,inputrec,fr,step,top_global->natoms,rerun_fr.box,
+ lambda,pres,vir,&prescorr,&enercorr,&dvdlcorr);
+ /* figure out how to rearrange the next 4 lines MRS 8/4/2009 */
+ enerd->term[F_DISPCORR] = enercorr;
+ enerd->term[F_EPOT] += enercorr;
+ enerd->term[F_PRES] += prescorr;
+ enerd->term[F_DVDL] += dvdlcorr;
+
+ epot = enerd->term[F_EPOT];
+ bEnergyOutOfBounds = FALSE;
+#if ( defined(GMX_IA32_SSE) || defined(GMX_X86_64_SSE) || defined(GMX_X86_64_SSE2) )
+ /* With SSE the energy can overflow, check for this */
+ if (gmx_mm_check_and_reset_overflow())
+ {
+ if (debug)
+ {
+ fprintf(debug,"Found an SSE overflow, assuming the energy is out of bounds\n");
+ }
+ bEnergyOutOfBounds = TRUE;
+ }
+#endif
+ /* If the compiler doesn't optimize this check away
+ * we catch the NAN energies.
+ * The epot>GMX_REAL_MAX check catches inf values,
+ * which should nicely result in embU=0 through the exp below,
+ * but it does not hurt to check anyhow.
+ */
+ /* Non-bonded Interaction usually diverge at r=0.
+ * With tabulated interaction functions the first few entries
+ * should be capped in a consistent fashion between
+ * repulsion, dispersion and Coulomb to avoid accidental
+ * negative values in the total energy.
+ * The table generation code in tables.c does this.
+ * With user tbales the user should take care of this.
+ */
+ if (epot != epot || epot > GMX_REAL_MAX)
+ {
+ bEnergyOutOfBounds = TRUE;
+ }
+ if (bEnergyOutOfBounds)
+ {
+ if (debug)
+ {
+ fprintf(debug,"\n time %.3f, step %d: non-finite energy %f, using exp(-bU)=0\n",t,step,epot);
+ }
+ embU = 0;
+ }
+ else
+ {
+ embU = exp(-beta*epot);
+ sum_embU += embU;
+ /* Determine the weighted energy contributions of each energy group */
+ e = 0;
+ sum_UgembU[e++] += epot*embU;
+ if (fr->bBHAM)
+ {
+ for(i=0; i<ngid; i++)
+ {
+ sum_UgembU[e++] +=
+ (enerd->grpp.ener[egBHAMSR][GID(i,gid_tp,ngid)] +
+ enerd->grpp.ener[egBHAMLR][GID(i,gid_tp,ngid)])*embU;
+ }
+ }
+ else
+ {
+ for(i=0; i<ngid; i++)
+ {
+ sum_UgembU[e++] +=
+ (enerd->grpp.ener[egLJSR][GID(i,gid_tp,ngid)] +
+ enerd->grpp.ener[egLJLR][GID(i,gid_tp,ngid)])*embU;
+ }
+ }
+ if (bDispCorr)
+ {
+ sum_UgembU[e++] += enerd->term[F_DISPCORR]*embU;
+ }
+ if (bCharge)
+ {
+ for(i=0; i<ngid; i++)
+ {
+ sum_UgembU[e++] +=
+ (enerd->grpp.ener[egCOULSR][GID(i,gid_tp,ngid)] +
+ enerd->grpp.ener[egCOULLR][GID(i,gid_tp,ngid)])*embU;
+ }
+ if (bRFExcl)
+ {
+ sum_UgembU[e++] += enerd->term[F_RF_EXCL]*embU;
+ }
+ if (EEL_FULL(fr->eeltype))
+ {
+ sum_UgembU[e++] += enerd->term[F_COUL_RECIP]*embU;
+ }
+ }
+ }
+
+ if (embU == 0 || beta*epot > bU_bin_limit)
+ {
+ bin[0]++;
+ }
+ else
+ {
+ i = (int)((bU_logV_bin_limit
+ - (beta*epot - logV + refvolshift))*invbinw
+ + 0.5);
+ if (i < 0)
+ {
+ i = 0;
+ }
+ if (i >= nbin)
+ {
+ realloc_bins(&bin,&nbin,i+10);
+ }
+ bin[i]++;
+ }
+
+ if (debug)
+ {
+ fprintf(debug,"TPI %7d %12.5e %12.5f %12.5f %12.5f\n",
+ step,epot,x_tp[XX],x_tp[YY],x_tp[ZZ]);
+ }
+
+ if (dump_pdb && epot <= dump_ener)
+ {
+ sprintf(str,"t%g_step%d.pdb",t,step);
+ sprintf(str2,"t: %f step %d ener: %f",t,step,epot);
+ write_sto_conf_mtop(str,str2,top_global,state->x,state->v,
+ inputrec->ePBC,state->box);
+ }
+ }
+ }
+
+ if (PAR(cr))
+ {
+ /* When running in parallel sum the energies over the processes */
+ gmx_sumd(1, &sum_embU, cr);
+ gmx_sumd(nener,sum_UgembU,cr);
+ }
+
+ frame++;
+ V_all += V;
+ VembU_all += V*sum_embU/nsteps;
+
+ if (fp_tpi)
+ {
+ if (bVerbose || frame%10==0 || frame<10)
+ {
+ fprintf(stderr,"mu %10.3e <mu> %10.3e\n",
+ -log(sum_embU/nsteps)/beta,-log(VembU_all/V_all)/beta);
+ }
+
+ fprintf(fp_tpi,"%10.3f %12.5e %12.5e %12.5e %12.5e",
+ t,
+ VembU_all==0 ? 20/beta : -log(VembU_all/V_all)/beta,
+ sum_embU==0 ? 20/beta : -log(sum_embU/nsteps)/beta,
+ sum_embU/nsteps,V);
+ for(e=0; e<nener; e++)
+ {
+ fprintf(fp_tpi," %12.5e",sum_UgembU[e]/nsteps);
+ }
+ fprintf(fp_tpi,"\n");
+ fflush(fp_tpi);
+ }
+
+ bNotLastFrame = read_next_frame(oenv, status,&rerun_fr);
+ } /* End of the loop */
+ runtime_end(runtime);
+
+ close_trj(status);
+
+ if (fp_tpi != NULL)
+ {
+ gmx_fio_fclose(fp_tpi);
+ }
+
+ if (fplog != NULL)
+ {
+ fprintf(fplog,"\n");
+ fprintf(fplog," <V> = %12.5e nm^3\n",V_all/frame);
+ fprintf(fplog," <mu> = %12.5e kJ/mol\n",-log(VembU_all/V_all)/beta);
+ }
+
+ /* Write the Boltzmann factor histogram */
+ if (PAR(cr))
+ {
+ /* When running in parallel sum the bins over the processes */
+ i = nbin;
+ global_max(cr,&i);
+ realloc_bins(&bin,&nbin,i);
+ gmx_sumd(nbin,bin,cr);
+ }
+ if (MASTER(cr))
+ {
+ fp_tpi = xvgropen(opt2fn("-tpid",nfile,fnm),
+ "TPI energy distribution",
+ "\\betaU - log(V/<V>)","count",oenv);
+ sprintf(str,"number \\betaU > %g: %9.3e",bU_bin_limit,bin[0]);
+ xvgr_subtitle(fp_tpi,str,oenv);
+ xvgr_legend(fp_tpi,2,(const char **)tpid_leg,oenv);
+ for(i=nbin-1; i>0; i--)
+ {
+ bUlogV = -i/invbinw + bU_logV_bin_limit - refvolshift + log(V_all/frame);
+ fprintf(fp_tpi,"%6.2f %10d %12.5e\n",
+ bUlogV,
+ (int)(bin[i]+0.5),
+ bin[i]*exp(-bUlogV)*V_all/VembU_all);
+ }
+ gmx_fio_fclose(fp_tpi);
+ }
+ sfree(bin);
+
+ sfree(sum_UgembU);
+
+ runtime->nsteps_done = frame*inputrec->nsteps;
+
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \defgroup module_options Extensible Handling of Options
+ * \ingroup group_utilitymodules
+ * \brief
+ * Provides functionality for handling options.
+ *
+ * <H3>Basic Use</H3>
+ *
+ * Basic interface for providing options is implemented by the Options class
+ * and classes defined in basicoptions.h for specifying individual options.
+ * Only these are needed if a class wants to provide a set of standard options.
+ *
+ * Values for options can be set using option parsers.
+ * Currently, only one is defined: CommandLineParser.
+ * As the name suggests, this parser gets values from command-line arguments.
+ * Multiple parsers can be used in sequence to provide option values from
+ * multiple sources; in such cases, if an option is provided in multiple
+ * sources, new values overwrite any values from previous sources.
+ *
+ * In most cases, it is desirable to be able to provide a help describing the
+ * available options. When creating an Options object and adding options, it
+ * is possible to add descriptions for individual options as well as for the
+ * whole set of options. These can then be used to write out a help using one
+ * of the provided help writers.
+ * Currently, only one is defined: AsciiHelpWriter
+ * (implementation is not yet complete).
+ *
+ * <H3>Advanced Use</H3>
+ *
+ * It is possible to extend the module with new option types and/or parsers for
+ * option values.
+ *
+ * To implement new option types, it is necessary to subclass the templates
+ * OptionTemplate and OptionStorageTemplate with the type of the values that
+ * the option should provide as the template argument. After this is done, it
+ * is possible to add options of this new type using Options::addOption().
+ *
+ * There is limited support for options that need to share information across
+ * instances, e.g., to store values in a shared external data structure or to
+ * provide a global option to set a common setting for all such options,
+ * provided by the OptionsGlobalProperties class. This mechanism is not
+ * generic, meaning that it is required to change the options module to add
+ * data to this structure.
+ *
+ * To implement new parsers, one can use OptionsAssigner, which provides an
+ * interface to set values in an Options object.
+ *
+ * There is also an interface to iterate over all options in an Options object.
+ * One should implement the OptionsVisitor interface, and then use
+ * OptionsIterator to apply this visitor to the Options object.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+/*! \file
+ * \brief
+ * Public API convenience header for handling of options.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_H
+#define GMX_OPTIONS_H
+
+#include "options/basicoptions.h"
+#include "options/options.h"
+
+#endif
--- /dev/null
+file(GLOB OPTIONS_SOURCES *.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${OPTIONS_SOURCES} PARENT_SCOPE)
+
+set(OPTIONS_PUBLIC_HEADERS
+ abstractoption.h
+ basicoptions.h
+ optionfiletype.h
+ optionflags.h
+ options.h)
+install(FILES ${OPTIONS_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/options
+ COMPONENT development)
+
+if (BUILD_TESTING)
+ add_subdirectory(tests)
+endif (BUILD_TESTING)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Defines gmx::AbstractOption and a related template.
+ *
+ * This header defines base classes for option settings that are used with
+ * Options::addOption(). These classes implement the "named parameter"
+ * idiom for specifying option properties.
+ *
+ * These classes also take care of creating and setting up the actual option
+ * objects.
+ *
+ * This header is needed directly only when implementing new option types,
+ * but methods of OptionTemplate are visible even to the normal user through
+ * its subclasses..
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ABSTRACTOPTION_H
+#define GMX_OPTIONS_ABSTRACTOPTION_H
+
+#include <string>
+#include <vector>
+
+#include "optionflags.h"
+
+namespace gmx
+{
+
+class AbstractOptionStorage;
+template <typename T> class OptionStorageTemplate;
+class Options;
+
+/*! \brief
+ * Abstract base class for specifying option properties.
+ *
+ * Concrete classes should normally not derive directly from this class,
+ * but from OptionTemplate instead. Classes derived from this class
+ * are mainly designed to implement the "named parameter" idiom. For
+ * efficiency and clarity, these classes should only store values provided to
+ * them. All error checking and memory management should be postponed to the
+ * point when the actual option is created.
+ *
+ * Subclasses should override createDefaultStorage() to create the correct type
+ * of storage object.
+ *
+ * \ingroup module_options
+ */
+class AbstractOption
+{
+ public:
+ // Virtual only for completeness, in normal use should not be needed.
+ virtual ~AbstractOption() { }
+
+ protected:
+ //! Initializes the name and default values for an option.
+ explicit AbstractOption(const char *name)
+ : _minValueCount(1), _maxValueCount(1),
+ _name(name), _descr(NULL)
+ { }
+
+ /*! \brief
+ * Creates a default storage object for the option.
+ *
+ * \param[in] options Option collection object.
+ * \param[out] storage The created storage object.
+ * \retval 0 if there are no errors.
+ *
+ * This method is called by when creating an option object
+ * The \p options object is used to implement global properties.
+ *
+ * Derived classes should implement the method to create an actual
+ * storage object and populate it with correct values.
+ *
+ * Should only be called by Options::addOption().
+ *
+ * \see AbstractOptionStorage::init()
+ */
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const = 0;
+
+ /*! \brief
+ * Creates the description string for the option.
+ *
+ * This function is virtual to allow derived classes to customize the
+ * description programmatically, e.g., by adding the list of allowed
+ * values.
+ * The default implementation simply returns the user-provided
+ * description.
+ */
+ virtual std::string createDescription() const
+ { return _descr ? _descr : ""; }
+
+ //! Sets the description for the option.
+ void setDescription(const char *descr) { _descr = descr; }
+ //! Sets a flag for the option.
+ void setFlag(OptionFlag flag) { _flags.set(flag); }
+ //! Clears a flag for the option.
+ void clearFlag(OptionFlag flag) { _flags.clear(flag); }
+ //! Returns true if the option is vector-valued.
+ bool isVector() const { return hasFlag(efVector); }
+ //! Sets the option to be vector-valued.
+ void setVector()
+ {
+ setFlag(efVector);
+ _minValueCount = 1;
+ if (_maxValueCount == 1)
+ {
+ _maxValueCount = 3;
+ }
+ }
+ //! Sets the required number of values for the option.
+ void setValueCount(int count)
+ {
+ if (!hasFlag(efVector))
+ {
+ _minValueCount = count;
+ }
+ _maxValueCount = count;
+ }
+
+ //! Minimum number of values required for the option.
+ int _minValueCount;
+ //! Maximum number of values allowed for the option.
+ int _maxValueCount;
+
+ private:
+ //! Returns true if a flag has been set.
+ bool hasFlag(OptionFlag flag) const { return _flags.test(flag); }
+
+ const char *_name;
+ //! Pointer to description of the option.
+ const char *_descr;
+ OptionFlags _flags;
+
+ /*! \brief
+ * Needed to initialize an AbstractOptionStorage object from this class
+ * without otherwise unnecessary accessors.
+ */
+ friend class AbstractOptionStorage;
+ /*! \brief
+ * Needed to be able to call createDefaultStorage().
+ */
+ friend class Options;
+};
+
+/*! \brief
+ * Templated base class for constructing concrete option settings classes.
+ *
+ * \tparam T Assignable type that stores a single option value.
+ * \tparam U Type of the derived class.
+ *
+ * This template is used as a base class like this:
+ * \code
+class ConcreteOption : public OptionTemplate<int, ConcreteOption>
+{
+ * \endcode
+ *
+ * All public functions in this class return \c *this casted to a reference to
+ * \p U.
+ *
+ * For examples of how to use classes derived from this class, see the class
+ * documentation for Options.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+template <typename T, class U>
+class OptionTemplate : public AbstractOption
+{
+ public:
+ //! Type that stores a single option value.
+ typedef T ValueType;
+ //! Alias for the derived class type.
+ typedef U MyClass;
+ //! Alias for the template class for use in base classes.
+ typedef OptionTemplate<T, U> MyBase;
+
+ /*! \brief
+ * Sets a description for the option.
+ *
+ * \param[in] descr Description to set.
+ *
+ * String in \p descr is copied when the option is created.
+ */
+ MyClass &description(const char *descr)
+ { setDescription(descr); return me(); }
+ //! Hides the option from normal help output.
+ MyClass &hidden() { setFlag(efHidden); return me(); }
+ //! Requires the option to be specified explicitly.
+ MyClass &required() { setFlag(efRequired); return me(); }
+ //! Allows the option to be specified multiple times.
+ MyClass &allowMultiple() { setFlag(efMulti); return me(); }
+ //! Requires exactly \p count values for the option.
+ MyClass &valueCount(int count) { setValueCount(count); return me(); }
+ //! Allows any number of values for the option.
+ MyClass &multiValue() { _maxValueCount = -1; return me(); }
+
+ /*! \brief
+ * Sets a default value for the option.
+ *
+ * \param[in] defaultValue Default value.
+ *
+ * If the option is never set, the default value is copied to the
+ * assigned storage. Note that if the option is not set and there
+ * is no default value, the storage is not altered, which can also be
+ * used to provide a default value. The latter method has to be used
+ * if the option can take multiple values.
+ * If required() is specified, only affects the default value shown in
+ * help output.
+ *
+ * \p defaultValue is copied when the option is created.
+ */
+ MyClass &defaultValue(const T &defaultValue)
+ { _defaultValue = &defaultValue; return me(); }
+ /*! \brief
+ * Stores value(s) in memory pointed by \p store.
+ *
+ * \param[in] store Storage for option value(s).
+ *
+ * The caller is responsible for allocating enough memory such that
+ * the any allowed number of values fits into the array pointed by
+ * \p store. If there is no maximum allowed number or if the maximum
+ * is inconveniently large, storeVector() should be used.
+ *
+ * The pointer provided should remain valid as long as the associated
+ * Options object exists.
+ */
+ MyClass &store(T *store)
+ { setFlag(efExternalStore); _store = store; return me(); }
+ /*! \brief
+ * Stores a pointer to array of values.
+ *
+ * \param[in] store Storage for a pointer to an array of values.
+ * \param[in] nvalptr Storage for the number of values.
+ *
+ * The pointer written to *store should be freed by the caller using
+ * delete[].
+ * Either of the input parameters can be NULL, in which case the
+ * corresponding value is not stored.
+ * The pointers provided should remain valid as long as the associated
+ * Options object exists.
+ *
+ * This function is deprecated, but may be convenient for interfacing
+ * with legacy code.
+ */
+ MyClass &storeArray(T **store, int *nvalptr)
+ { setFlag(efExternalStoreArray); _storeArray = store; _nvalptr = nvalptr; return me(); }
+ /*! \brief
+ * Stores option values in the provided vector.
+ *
+ * \param[in] store Vector to store option values in.
+ *
+ * The caller should not make any assumptions about the contents of the
+ * vector while options are being parsed.
+ *
+ * The pointer provided should remain valid as long as the associated
+ * Options object exists.
+ */
+ MyClass &storeVector(std::vector<T> *store)
+ { setFlag(efExternalValueVector); _storeVector = store; return me(); }
+
+ protected:
+ //! Initializes the name and default values for an option.
+ explicit OptionTemplate(const char *name)
+ : AbstractOption(name), _defaultValue(NULL), _store(NULL),
+ _storeArray(NULL), _nvalptr(NULL), _storeVector(NULL)
+ { }
+
+ /*! \brief
+ * Returns a pointer to user-specified default value, or NULL if there
+ * is none.
+ */
+ const T *defaultValue() const { return _defaultValue; }
+ //! Returns \p *this casted into MyClass to reduce typing.
+ MyClass &me() { return static_cast<MyClass &>(*this); }
+
+ private:
+ const T *_defaultValue;
+ T *_store;
+ T **_storeArray;
+ int *_nvalptr;
+ std::vector<T> *_storeVector;
+
+ /*! \brief
+ * Needed to initialize storage from this class without otherwise
+ * unnecessary accessors.
+ */
+ friend class OptionStorageTemplate<T>;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::AbstractOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/abstractoptionstorage.h"
+
+#include <cassert>
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/options/optionflags.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AbstractOptionStorage
+ */
+
+AbstractOptionStorage::AbstractOptionStorage()
+ : _minValueCount(0), _maxValueCount(0), _currentValueCount(-1),
+ _options(NULL)
+{
+}
+
+AbstractOptionStorage::~AbstractOptionStorage()
+{
+}
+
+int AbstractOptionStorage::init(const AbstractOption &settings,
+ Options *options)
+{
+ // We add user-provided flags to the ones possibly set by the subclass.
+ _flags |= settings._flags;
+ _minValueCount = settings._minValueCount;
+ _maxValueCount = settings._maxValueCount;
+ _options = options;
+
+ // If the maximum number of values is not known, storage to
+ // caller-allocated memory is unsafe.
+ if ((_maxValueCount < 0 || hasFlag(efMulti)) && hasFlag(efExternalStore))
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Cannot set user-allocated storage for arbitrary number of values");
+ }
+ // Check that user has not provided incorrect values for vectors.
+ if (hasFlag(efVector) && (_minValueCount > 1 || _maxValueCount < 1))
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Inconsistent value counts for vector values");
+ }
+
+ if (settings._name != NULL)
+ {
+ _name = settings._name;
+ }
+ _descr = settings.createDescription();
+
+ return 0;
+}
+
+int AbstractOptionStorage::startSource()
+{
+ setFlag(efHasDefaultValue);
+ return 0;
+}
+
+int AbstractOptionStorage::startSet(AbstractErrorReporter *errors)
+{
+ if (hasFlag(efHasDefaultValue))
+ {
+ clearFlag(efHasDefaultValue);
+ clear();
+ }
+ else if (isSet() && !hasFlag(efMulti))
+ {
+ errors->error("Option specified multiple times");
+ return eeInvalidInput;
+ }
+ _currentValueCount = 0;
+ return 0;
+}
+
+int AbstractOptionStorage::appendValue(const std::string &value,
+ AbstractErrorReporter *errors)
+{
+ assert(_currentValueCount >= 0);
+ if (!hasFlag(efConversionMayNotAddValues) && _maxValueCount >= 0
+ && _currentValueCount >= _maxValueCount)
+ {
+ errors->warning("Ignoring extra value: " + value);
+ return eeInvalidInput;
+ }
+ return convertValue(value, errors);
+}
+
+int AbstractOptionStorage::finishSet(AbstractErrorReporter *errors)
+{
+ assert(_currentValueCount >= 0);
+
+ setFlag(efSet);
+ // TODO: Remove invalid values if there are too few
+ int rc = processSet(_currentValueCount, errors);
+ if (!hasFlag(efDontCheckMinimumCount)
+ && _currentValueCount < _minValueCount)
+ {
+ errors->error("Too few (valid) values");
+ rc = eeInvalidInput;
+ }
+ _currentValueCount = -1;
+ return rc;
+}
+
+int AbstractOptionStorage::finish(AbstractErrorReporter *errors)
+{
+ assert(_currentValueCount == -1);
+ int rc = processAll(errors);
+ if (hasFlag(efRequired) && !isSet())
+ {
+ errors->error("Required option '" + _name + "' not set");
+ rc = eeInconsistentInput;
+ }
+ return rc;
+}
+
+int AbstractOptionStorage::incrementValueCount()
+{
+ if (_currentValueCount == -1)
+ {
+ return 0;
+ }
+ if (_maxValueCount >= 0 && _currentValueCount >= _maxValueCount)
+ {
+ return eeInvalidInput;
+ }
+ ++_currentValueCount;
+ return 0;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AbstractOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ABSTRACTOPTIONSTORAGE_H
+#define GMX_OPTIONS_ABSTRACTOPTIONSTORAGE_H
+
+#include <string>
+
+#include "optionflags.h"
+
+namespace gmx
+{
+
+class AbstractErrorReporter;
+class AbstractOption;
+class Options;
+
+/*! \libinternal \brief
+ * Abstract base class for converting, validating, and storing option values.
+ *
+ * This class should normally not be subclassed directly, but the
+ * OptionStorageTemplate should be used instead. The templated class provides
+ * basic functionality for most of the pure virtual methods, and also
+ * integrates well with option setting objects derived from OptionTemplate.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ *
+ * \internal
+ * This class really consists of two parts: the public interface that is
+ * used by the internal implementation of the options module, and the
+ * interface that derived classes use to provide type-dependent functionality.
+ * The latter consists of a few pure virtual methods, of which a few simple
+ * query methods are also part of the module-internal interface, others are
+ * protected and called by the non-virtual methods when needed.
+ * The reason why these two roles are in one class is twofold:
+ * -# Both the derived classes and the internal module implementation may need
+ * access to the same information like the allowed number of values and the
+ * name of the option.
+ * -# Having only one class is consistent with the structure used for options
+ * settings objects: there is very direct correspondence between
+ * AbstractOption and AbstractOptionStorage and between OptionTemplate and
+ * OptionStorageTemplate.
+ */
+class AbstractOptionStorage
+{
+ public:
+ virtual ~AbstractOptionStorage();
+
+ /*! \brief
+ * Initializes the storage object from the settings object.
+ *
+ * \param[in] settings Option settings.
+ * \param[in] options Option collection that will contain the
+ * option.
+ * \retval 0 on success.
+ */
+ int init(const AbstractOption &settings, Options *options);
+
+ //! Returns true if the option has been set.
+ bool isSet() const { return hasFlag(efSet); }
+ //! Returns true if the option is a boolean option.
+ bool isBoolean() const { return hasFlag(efBoolean); }
+ //! Returns true if the option is a file option.
+ bool isFile() const { return hasFlag(efFile); }
+ //! Returns true if the option is a hidden option.
+ bool isHidden() const { return hasFlag(efHidden); }
+ //! Returns the name of the option.
+ const std::string &name() const { return _name; }
+ //! Returns the description of the option.
+ const std::string &description() const { return _descr; }
+ /*! \brief
+ * Returns a short string describing the type of the option.
+ *
+ * The caller is free to discard the returned string.
+ */
+ virtual const char *typeString() const = 0;
+ /*! \brief
+ * Returns the number of option values added so far.
+ */
+ virtual int valueCount() const = 0;
+ /*! \brief
+ * Returns the i'th value formatted as a string.
+ */
+ virtual std::string formatValue(int i) const = 0;
+
+ /*! \brief
+ * Starts adding values from a new source for the option.
+ *
+ * \retval 0 on success.
+ *
+ * This marks the vurrent value of the option as a default value,
+ * causing next call to startSet() to clear it. This allows values
+ * from the new source to overwrite old values.
+ */
+ int startSource();
+ /*! \brief
+ * Starts adding a new set of values for the option.
+ *
+ * \param[in] errors Error reporter for errors.
+ * \retval 0 on success.
+ *
+ * If the parameter is specified multiple times, startSet() should be
+ * called before the values for each instance.
+ */
+ int startSet(AbstractErrorReporter *errors);
+ /*! \brief
+ * Adds a new value for the option, converting it from a string.
+ *
+ * \param[in] value String value to convert.
+ * \param[in] errors Error reporter for errors.
+ * \retval 0 if the value was successfully converted and added.
+ */
+ int appendValue(const std::string &value,
+ AbstractErrorReporter *errors);
+ /*! \brief
+ * Performs validation and/or actions once a set of values has been
+ * added.
+ *
+ * \param[in] errors Error reporter for errors.
+ * \retval 0 on success.
+ *
+ * If the parameter is specified multiple times, finishSet() should be
+ * called after the values for each instance.
+ */
+ int finishSet(AbstractErrorReporter *errors);
+ /*! \brief
+ * Performs validation and/or actions once all values have been added.
+ *
+ * \param[in] errors Error reporter for errors.
+ * \retval 0 on success.
+ *
+ * This function should be called after all values have been provided
+ * with appendValue(). Calls finishSet() if needed.
+ */
+ int finish(AbstractErrorReporter *errors);
+
+ protected:
+ //! Creates an uninitialized storage object.
+ AbstractOptionStorage();
+
+ //! Returns true if the given flag is set.
+ bool hasFlag(OptionFlag flag) const { return _flags.test(flag); }
+ //! Sets the given flag.
+ void setFlag(OptionFlag flag) { return _flags.set(flag); }
+ //! Clears the given flag.
+ void clearFlag(OptionFlag flag) { return _flags.clear(flag); }
+
+ //! Returns the minimum number of values required in one set.
+ int minValueCount() const { return _minValueCount; }
+ //! Returns the maximum allowed number of values in one set (-1 = no limit).
+ int maxValueCount() const { return _maxValueCount; }
+
+ //! Returns the Options object that houses the option.
+ Options &hostOptions() { return *_options; }
+ //! \copydoc hostOptions()
+ const Options &hostOptions() const { return *_options; }
+
+ /*! \brief
+ * Removes all values from the storage.
+ *
+ * This function is called also before the first value is added,
+ * allowing the storage to set a default value in initialization.
+ */
+ virtual void clear() = 0;
+ /*! \brief
+ * Adds a new value, converting it from a string.
+ *
+ * \param[in] value String value to convert.
+ * \param[in] errors Error reporter object.
+ * \retval 0 if the value was successfully converted.
+ *
+ * This function may be called multiple times if the underlying
+ * option is defined to accept multiple values.
+ */
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors) = 0;
+ /*! \brief
+ * Performs validation and/or actions once a set of values has been
+ * added.
+ *
+ * \param[in] nvalues Number of values added since the previous call
+ * to finishSet().
+ * \param[in] errors Error reporter object.
+ * \retval 0 on success.
+ *
+ * This function may be called multiple times of the underlying option
+ * can be specified multiple times.
+ */
+ virtual int processSet(int nvalues, AbstractErrorReporter *errors) = 0;
+ /*! \brief
+ * Performs validation and/or actions once all values have been added.
+ *
+ * \param[in] errors Error reporter object.
+ * \retval 0 if final option values are valid.
+ *
+ * This function is always called once.
+ */
+ virtual int processAll(AbstractErrorReporter *errors) = 0;
+
+ /*! \brief
+ * Increments the number of values for the current set.
+ *
+ * \retval 0 on success.
+ * \retval ::eeInvalidInput if the maximum value count has been reached.
+ */
+ int incrementValueCount();
+
+ private:
+ std::string _name;
+ std::string _descr;
+ //! Flags for the option.
+ OptionFlags _flags;
+ //! Minimum number of values required (in one set).
+ int _minValueCount;
+ //! Maximum allowed number of values (in one set), or -1 if no limit.
+ int _maxValueCount;
+ //! Number of values added so far to the current set, or -1 if not in one.
+ int _currentValueCount;
+ //! Parent Options object.
+ Options *_options;
+
+ // Disallow copy and assign.
+ AbstractOptionStorage(const AbstractOptionStorage &);
+ void operator =(const AbstractOptionStorage &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::AsciiHelpWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ASCIIHELPWRITER_IMPL_HPP
+#define GMX_OPTIONS_ASCIIHELPWRITER_IMPL_HPP
+
+#include "asciihelpwriter.h"
+
+#include "../utility/flags.h"
+
+namespace gmx
+{
+
+class Options;
+
+/*! \internal \brief
+ * Private implementation class for AsciiHelpWriter.
+ *
+ * \ingroup module_options
+ */
+class AsciiHelpWriter::Impl
+{
+ public:
+ /*! \brief
+ * Possible flags for controlling output.
+ */
+ enum Flag
+ {
+ efShowDescriptions = 1<<0,
+ efShowHidden = 1<<1
+ };
+
+ //! Sets the Options object to use for generating help.
+ explicit Impl(const Options &options);
+
+ //! Options object to use for generating help.
+ const Options &_options;
+ //! Flags for controlling what to write.
+ FlagsTemplate<Flag> _flags;
+};
+
+/*! \internal \brief
+ * Helper object for writing section descriptions to help.
+ *
+ * \ingroup module_options
+ */
+class AsciiDescriptionWriter : public OptionsVisitor
+{
+ public:
+ //! Creates a helper object for writing section descriptions.
+ explicit AsciiDescriptionWriter(FILE *fp) : _fp(fp) {}
+
+ virtual void visitSubSection(const Options §ion);
+ virtual void visitOption(const OptionInfo & /*option*/) { }
+
+ private:
+ FILE *_fp;
+};
+
+/*! \internal \brief
+ * Helper object for writing help for file parameters.
+ *
+ * \ingroup module_options
+ */
+class AsciiFileParameterWriter : public OptionsVisitor
+{
+ public:
+ //! Creates a helper object for writing file parameters.
+ explicit AsciiFileParameterWriter(FILE *fp)
+ : _fp(fp), _bFirst(true)
+ {
+ }
+
+ //! Returns true if anything was written out.
+ bool didOutput() const { return !_bFirst; }
+
+ virtual void visitSubSection(const Options §ion);
+ virtual void visitOption(const OptionInfo &option);
+
+ private:
+ FILE *_fp;
+ bool _bFirst;
+};
+
+/*! \internal \brief
+ * Helper object for writing help for non-file parameters.
+ *
+ * \ingroup module_options
+ */
+class AsciiParameterWriter : public OptionsVisitor
+{
+ public:
+ //! Creates a helper object for writing non-file parameters.
+ explicit AsciiParameterWriter(FILE *fp)
+ : _fp(fp), _bFirst(true), _bShowHidden(false)
+ {
+ }
+
+ //! Sets the writer to show hidden options.
+ void setShowHidden(bool bSet)
+ {
+ _bShowHidden = bSet;
+ }
+ //! Returns true if anything was written out.
+ bool didOutput() const { return !_bFirst; }
+
+ virtual void visitSubSection(const Options §ion);
+ virtual void visitOption(const OptionInfo &option);
+
+ private:
+ FILE *_fp;
+ bool _bFirst;
+ bool _bShowHidden;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::AsciiHelpWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/asciihelpwriter.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionsvisitor.h"
+
+#include "asciihelpwriter-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * AsciiHelpWriter::Impl
+ */
+
+AsciiHelpWriter::Impl::Impl(const Options &options)
+ : _options(options)
+{
+}
+
+
+/********************************************************************
+ * AsciiDescriptionWriter
+ */
+
+void AsciiDescriptionWriter::visitSubSection(const Options §ion)
+{
+ if (!section.description().empty())
+ {
+ fprintf(_fp, "\n");
+ const std::string &title = section.title();
+ if (!title.empty())
+ {
+ fprintf(_fp, "%s\n\n", title.c_str());
+ }
+ // TODO: Wrap lines and do markup substitutions.
+ fprintf(_fp, "%s\n\n", section.description().c_str());
+ }
+ OptionsIterator(section).acceptSubSections(this);
+}
+
+
+/********************************************************************
+ * AsciiFileParameterWriter
+ */
+
+void AsciiFileParameterWriter::visitSubSection(const Options §ion)
+{
+ OptionsIterator iterator(section);
+ iterator.acceptSubSections(this);
+ iterator.acceptOptions(this);
+}
+
+void AsciiFileParameterWriter::visitOption(const OptionInfo &option)
+{
+ if (!option.isFile())
+ {
+ return;
+ }
+
+ if (_bFirst)
+ {
+ fprintf(_fp, "%6s %12s %-12s %s\n",
+ "Option", "Filename", "Type", "Description");
+ fprintf(_fp, "------------------------------------------------------------\n");
+ _bFirst = false;
+ }
+
+ std::string optionLine("-");
+ optionLine.reserve(30 + option.description().size());
+ optionLine.append(option.name()).append(" ");
+ if (optionLine.size() < 11)
+ {
+ optionLine.resize(11, ' ');
+ }
+ bool bTypePrinted = false;
+ size_t lineStart = 0;
+ for (int i = 0; i < option.valueCount(); ++i)
+ {
+ if (i > 0)
+ {
+ optionLine.append("\n");
+ lineStart = optionLine.size();
+ optionLine.append(11, ' ');
+ }
+ optionLine.append(option.formatValue(i)).append(" ");
+ // TODO: Do eliding
+ if (optionLine.size() <= lineStart + 21)
+ {
+ optionLine.resize(lineStart + 21, ' ');
+ if (!bTypePrinted)
+ {
+ optionLine.append(option.type()).append(" ");
+ bTypePrinted = true;
+ }
+ }
+ }
+ if (!bTypePrinted)
+ {
+ optionLine.append("\n");
+ lineStart = optionLine.size();
+ optionLine.append(21, ' ');
+ optionLine.append(option.type()).append(" ");
+ }
+ if (optionLine.size() > lineStart + 34)
+ {
+ if (!option.description().empty())
+ {
+ optionLine.append("\n");
+ optionLine.append(34, ' ');
+ }
+ }
+ else
+ {
+ optionLine.resize(lineStart + 34, ' ');
+ }
+ // TODO: Markup substitution.
+ optionLine.append(option.description());
+ // TODO: Wrap lines.
+ fprintf(_fp, "%s\n", optionLine.c_str());
+}
+
+
+/********************************************************************
+ * AsciiParameterWriter
+ */
+
+void AsciiParameterWriter::visitSubSection(const Options §ion)
+{
+ OptionsIterator iterator(section);
+ iterator.acceptSubSections(this);
+ iterator.acceptOptions(this);
+}
+
+void AsciiParameterWriter::visitOption(const OptionInfo &option)
+{
+ if (option.isFile() || (!_bShowHidden && option.isHidden()))
+ {
+ return;
+ }
+
+ if (_bFirst)
+ {
+ fprintf(_fp, "%-12s %-6s %-6s %s\n",
+ "Option", "Type", "Value", "Description");
+ fprintf(_fp, "----------------------------------------------------\n");
+ _bFirst = false;
+ }
+
+ std::string optionLine("-");
+ optionLine.reserve(30 + option.description().size());
+ if (option.isBoolean())
+ {
+ optionLine.append("[no]");
+ }
+ optionLine.append(option.name()).append(" ");
+ if (optionLine.size() < 13)
+ {
+ optionLine.resize(13, ' ');
+ }
+ optionLine.append(option.type()).append(" ");
+ if (optionLine.size() < 20)
+ {
+ optionLine.resize(20, ' ');
+ }
+ optionLine.append(option.formatValues()).append(" ");
+ if (optionLine.size() > 28)
+ {
+ // TODO: Wrap lines / do eliding
+ if (!option.description().empty())
+ {
+ optionLine.append("\n");
+ optionLine.append(28, ' ');
+ }
+ }
+ else
+ {
+ optionLine.resize(28, ' ');
+ }
+ // TODO: Markup substitution.
+ optionLine.append(option.description());
+ // TODO: Wrap lines.
+ fprintf(_fp, "%s\n", optionLine.c_str());
+}
+
+/********************************************************************
+ * AsciiHelpWriter
+ */
+
+AsciiHelpWriter::AsciiHelpWriter(const Options &options)
+ : _impl(new Impl(options))
+{
+}
+
+AsciiHelpWriter::~AsciiHelpWriter()
+{
+ delete _impl;
+}
+
+AsciiHelpWriter &AsciiHelpWriter::setShowHidden(bool bSet)
+{
+ _impl->_flags.set(Impl::efShowHidden, bSet);
+ return *this;
+}
+
+AsciiHelpWriter &AsciiHelpWriter::setShowDescriptions(bool bSet)
+{
+ _impl->_flags.set(Impl::efShowDescriptions, bSet);
+ return *this;
+}
+
+int AsciiHelpWriter::writeHelp(FILE *fp)
+{
+ if (_impl->_flags.test(Impl::efShowDescriptions))
+ {
+ fprintf(fp, "DESCRIPTION\n"
+ "-----------\n");
+ AsciiDescriptionWriter(fp).visitSubSection(_impl->_options);
+ }
+ {
+ AsciiFileParameterWriter writer(fp);
+ writer.visitSubSection(_impl->_options);
+ if (writer.didOutput())
+ {
+ fprintf(fp, "\n");
+ }
+ }
+ {
+ AsciiParameterWriter writer(fp);
+ writer.setShowHidden(_impl->_flags.test(Impl::efShowHidden));
+ writer.visitSubSection(_impl->_options);
+ if (writer.didOutput())
+ {
+ fprintf(fp, "\n");
+ }
+ }
+ return 0;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AsciiHelpWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ASCIIHELPWRITER_H
+#define GMX_OPTIONS_ASCIIHELPWRITER_H
+
+#include <cstdio>
+
+namespace gmx
+{
+
+class Options;
+
+/*! \brief
+ * Writes help information for Options in ascii format.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class AsciiHelpWriter
+{
+ public:
+ /*! \brief
+ * Creates an object that writer ascii-formatted help for Options.
+ *
+ * \param[in] options Options for which help should be printed.
+ */
+ explicit AsciiHelpWriter(const Options &options);
+ ~AsciiHelpWriter();
+
+ /*! \brief
+ * Sets whether hidden options are shown in the help.
+ */
+ AsciiHelpWriter &setShowHidden(bool bShow);
+ /*! \brief
+ * Sets whether long descriptions for sections are shown in the help.
+ */
+ AsciiHelpWriter &setShowDescriptions(bool bShow);
+
+ /*! \brief
+ * Writes the help.
+ *
+ * \param[in] fp File to write the help to.
+ * \retval 0 on success.
+ */
+ int writeHelp(FILE *fp);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ AsciiHelpWriter(const AsciiHelpWriter &);
+ void operator =(const AsciiHelpWriter &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in basicoptions.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/basicoptions.h"
+
+#include "basicoptionstorage.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * BooleanOption
+ */
+
+int BooleanOption::createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const
+{
+ return createOptionStorage<BooleanOption, BooleanOptionStorage>(this, options, storage);
+}
+
+/********************************************************************
+ * IntegerOption
+ */
+
+int IntegerOption::createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const
+{
+ return createOptionStorage<IntegerOption, IntegerOptionStorage>(this, options, storage);
+}
+
+/********************************************************************
+ * DoubleOption
+ */
+
+int DoubleOption::createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const
+{
+ return createOptionStorage<DoubleOption, DoubleOptionStorage>(this, options, storage);
+}
+
+/********************************************************************
+ * StringOption
+ */
+
+int StringOption::createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const
+{
+ return createOptionStorage<StringOption, StringOptionStorage>(this, options, storage);
+}
+
+std::string StringOption::createDescription() const
+{
+ std::string value(MyBase::createDescription());
+
+ if (_enumValues != NULL)
+ {
+ value.append(": ");
+ for (int i = 0; _enumValues[i] != NULL; ++i)
+ {
+ value.append(_enumValues[i]);
+ if (_enumValues[i + 1] != NULL)
+ {
+ value.append(_enumValues[i + 2] != NULL ? ", " : ", or ");
+ }
+ }
+ }
+ return value;
+}
+
+/********************************************************************
+ * FileNameOption
+ */
+
+int FileNameOption::createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const
+{
+ return createOptionStorage<FileNameOption, FileNameOptionStorage>(this, options, storage);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares option settings objects for basic option types.
+ *
+ * Together with options.h, this header forms the part of the public API
+ * that most classes will use to provide options.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_BASICOPTIONS_H
+#define GMX_OPTIONS_BASICOPTIONS_H
+
+#include <cassert>
+
+#include <string>
+
+#include "abstractoption.h"
+#include "optionfiletype.h"
+
+namespace gmx
+{
+
+class IntegerOptionStorage;
+class DoubleOptionStorage;
+class StringOptionStorage;
+class FileNameOptionStorage;
+class Options;
+
+/*! \addtogroup module_options
+ * \{
+ */
+
+/*! \brief
+ * Specifies an option that provides boolean values.
+ *
+ * Example:
+ * \code
+bool bPBC;
+using gmx::BooleanOption;
+options.addOption(BooleanOption("pbc").store(&bPBC));
+ * \endcode
+ *
+ * \inpublicapi
+ */
+class BooleanOption : public OptionTemplate<bool, BooleanOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit BooleanOption(const char *name) : MyBase(name)
+ {
+ setFlag(efBoolean);
+ }
+
+ protected:
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const;
+};
+
+/*! \brief
+ * Specifies an option that provides integer values.
+ *
+ * Examples:
+ * \code
+using gmx::IntegerOption;
+// Simple option
+int rcut = 0;
+options.addOption(IntegerOption("rcut").store(&rcut));
+// Vector-valued option
+int box[3] = {1, 1, 1}; // Default value
+options.addOption(IntegerOption("box").store(box).vector());
+ * \endcode
+ *
+ * \inpublicapi
+ */
+class IntegerOption : public OptionTemplate<int, IntegerOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit IntegerOption(const char *name) : MyBase(name) {}
+
+ /*! \brief
+ * Sets the option to return a vector value.
+ *
+ * A vector value returns a fixed number of values, the default being
+ * three (can be changed with valueCount()). However, it also accepts
+ * a single value, in which case the value is used to fill the whole
+ * vector.
+ */
+ MyClass &vector() { setVector(); return me(); }
+
+ protected:
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const;
+
+ /*! \brief
+ * Needed to initialize IntegerOptionStorage from this class without
+ * otherwise unnecessary accessors.
+ */
+ friend class IntegerOptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides floating-point (double) values.
+ *
+ * \inpublicapi
+ */
+class DoubleOption : public OptionTemplate<double, DoubleOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit DoubleOption(const char *name) : MyBase(name), _bTime(false)
+ {
+ }
+
+ //! \copydoc IntegerOption::vector()
+ MyClass &vector() { setVector(); return me(); }
+ /*! \brief
+ * Sets the option to obey time conversion rules.
+ */
+ MyClass &timeValue() { _bTime = true; return me(); }
+
+ private:
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const;
+
+ bool _bTime;
+
+ /*! \brief
+ * Needed to initialize DoubleOptionStorage from this class without
+ * otherwise unnecessary accessors.
+ */
+ friend class DoubleOptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides string values.
+ *
+ * Examples:
+ * \code
+using gmx::StringOption;
+// Simple option
+std::string str;
+options.addOption(StringOption("str").store(&str));
+// Option that only accepts predefined values
+const char * const allowed[] = { "atom", "residue", "molecule", NULL };
+std::string str;
+int type;
+options.addOption(StringOption("type").enumValue(allowed).store(&str)
+ .storeEnumIndex(&type));
+ * \endcode
+ *
+ * \inpublicapi
+ */
+class StringOption : public OptionTemplate<std::string, StringOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit StringOption(const char *name)
+ : MyBase(name), _enumValues(NULL), _defaultEnumIndex(-1),
+ _enumIndexStore(NULL)
+ {}
+
+ /*! \brief
+ * Sets the option to only accept one of a fixed set of strings.
+ *
+ * \param[in] values Array of strings to accept, terminated with a
+ * NULL value.
+ *
+ * Also accepts prefixes of the strings; if a prefix matches more than
+ * one of the possible strings, the shortest one is used (in a tie, the
+ * first one is).
+ *
+ * It is not possible to provide multiple values for an option with
+ * this property set, i.e., valueCount() and similar attributes cannot
+ * be set.
+ *
+ * The strings are copied once the option is created.
+ */
+ MyClass &enumValue(const char *const *values)
+ { _enumValues = values; return me(); }
+ /*! \brief
+ * Sets the default value using an index into the enumeration table.
+ *
+ * Cannot be specified without enumValue().
+ */
+ MyClass &defaultEnumIndex(int index)
+ { assert(index >= 0); _defaultEnumIndex = index; return me(); }
+ /*! \brief
+ * Stores the index of the selected value into the provided memory
+ * location.
+ *
+ * The index (zero-based) of the selected value in the array \p values
+ * provided to enumValues() is written into \p *store after the
+ * option gets its value. If the option has not been provided,
+ * and there is no default value, -1 is stored.
+ *
+ * Cannot be specified without enumValue().
+ */
+ MyClass &storeEnumIndex(int *store)
+ { _enumIndexStore = store; return me(); }
+
+ protected:
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const;
+ virtual std::string createDescription() const;
+
+ private:
+ const char *const *_enumValues;
+ int _defaultEnumIndex;
+ int *_enumIndexStore;
+
+ /*! \brief
+ * Needed to initialize StringOptionStorage from this class without
+ * otherwise unnecessary accessors.
+ */
+ friend class StringOptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides file names.
+ *
+ * \inpublicapi
+ */
+class FileNameOption : public OptionTemplate<std::string, FileNameOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit FileNameOption(const char *name)
+ : MyBase(name), _filetype(eftUnknown)
+ {
+ setFlag(efFile);
+ }
+
+ /*! \brief
+ * Sets the type of the file this option accepts.
+ *
+ * This attribute must be provided.
+ */
+ MyClass &filetype(OptionFileType type)
+ { _filetype = type; return me(); }
+ //! Tells that the file provided by this option is used read-only.
+ MyClass &readOnly()
+ { setFlag(efFileRead); clearFlag(efFileWrite); return me(); }
+ //! Tells that the file provided by this option is used write-only.
+ MyClass &writeOnly()
+ { setFlag(efFileWrite); clearFlag(efFileRead); return me(); }
+ /*! \brief
+ * Tells that the file provided by this option is used for reading and
+ * writing.
+ */
+ MyClass &readwrite()
+ { setFlag(efFileRead); setFlag(efFileWrite); return me(); }
+ /*! \brief
+ * Tells that the file will be looked up in library directories in
+ * addition to working directory.
+ */
+ MyClass &libraryFile() { setFlag(efFileLibrary); return me(); }
+
+ protected:
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const;
+
+ private:
+ OptionFileType _filetype;
+
+ /*! \brief
+ * Needed to initialize FileNameOptionStorage from this class without
+ * otherwise unnecessary accessors.
+ */
+ friend class FileNameOptionStorage;
+};
+
+/*!\}*/
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in basicoptionstorage.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "basicoptionstorage.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/globalproperties.h"
+#include "gromacs/options/options.h"
+
+template <typename T> static
+int expandVector(int length, int *nvalues, gmx::AbstractErrorReporter *errors,
+ std::vector<T> *values)
+{
+ if (length > 0 && *nvalues > 0 && *nvalues != length)
+ {
+ if (*nvalues != 1)
+ {
+ char err_buf[256];
+ std::sprintf(err_buf, "Expected 1 or %d values, got %d",
+ length, *nvalues);
+ errors->error(err_buf);
+ values->resize(values->size() - *nvalues);
+ return gmx::eeInvalidInput;
+ }
+ const T &value = (*values)[values->size() - 1];
+ values->resize(values->size() + length - 1, value);
+ *nvalues = length;
+ }
+ return 0;
+}
+
+namespace gmx
+{
+
+/********************************************************************
+ * BooleanOptionStorage
+ */
+
+std::string BooleanOptionStorage::formatValue(int i) const
+{
+ bool value = values()[i];
+ return value ? "yes" : "no";
+}
+
+int BooleanOptionStorage::convertValue(const std::string &value,
+ AbstractErrorReporter *errors)
+{
+ // TODO: Case-independence
+ if (value == "1" || value == "yes" || value == "true")
+ {
+ addValue(true);
+ return 0;
+ }
+ else if (value == "0" || value == "no" || value == "false")
+ {
+ addValue(false);
+ return 0;
+ }
+ errors->error("Invalid value: '" + value + "'; supported values are: 1, 0, yes, no, true, false");
+ return eeInvalidInput;
+}
+
+/********************************************************************
+ * IntegerOptionStorage
+ */
+
+IntegerOptionStorage::IntegerOptionStorage()
+{
+}
+
+std::string IntegerOptionStorage::formatValue(int i) const
+{
+ char buf[64];
+ int value = values()[i];
+ std::sprintf(buf, "%d", value);
+ return std::string(buf);
+}
+
+int IntegerOptionStorage::convertValue(const std::string &value,
+ AbstractErrorReporter *errors)
+{
+ const char *ptr = value.c_str();
+ char *endptr = NULL;
+ long int ival = std::strtol(ptr, &endptr, 10);
+ if (*endptr == '\0')
+ {
+ addValue(ival);
+ return 0;
+ }
+ errors->error("Invalid value: " + value);
+ return eeInvalidInput;
+}
+
+int IntegerOptionStorage::processSet(int nvalues, AbstractErrorReporter *errors)
+{
+ if (hasFlag(efVector))
+ {
+ int rc = expandVector(maxValueCount(), &nvalues, errors, &values());
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return MyBase::processSet(nvalues, errors);
+}
+
+/********************************************************************
+ * DoubleOptionStorage
+ */
+
+DoubleOptionStorage::DoubleOptionStorage()
+ : _bTime(false)
+{
+}
+
+int DoubleOptionStorage::init(const DoubleOption &settings, Options *options)
+{
+ _bTime = settings._bTime;
+ if (_bTime)
+ {
+ options->globalProperties().request(eogpTimeScaleFactor);
+ }
+ return MyBase::init(settings, options);
+}
+
+const char *DoubleOptionStorage::typeString() const
+{
+ return hasFlag(efVector) > 0 ? "vector" : (_bTime ? "time" : "double");
+}
+
+std::string DoubleOptionStorage::formatValue(int i) const
+{
+ char buf[64];
+ double value = values()[i];
+ if (_bTime)
+ {
+ double factor = hostOptions().globalProperties().timeScaleFactor();
+ value /= factor;
+ }
+ std::sprintf(buf, "%g", value);
+ return std::string(buf);
+}
+
+int DoubleOptionStorage::convertValue(const std::string &value,
+ AbstractErrorReporter *errors)
+{
+ const char *ptr = value.c_str();
+ char *endptr = NULL;
+ double dval = std::strtod(ptr, &endptr);
+ if (*endptr == '\0')
+ {
+ addValue(dval);
+ return 0;
+ }
+ errors->error("Invalid value: " + value);
+ return eeInvalidInput;
+}
+
+int DoubleOptionStorage::processSet(int nvalues, AbstractErrorReporter *errors)
+{
+ if (hasFlag(efVector))
+ {
+ int rc = expandVector(maxValueCount(), &nvalues, errors, &values());
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ return MyBase::processSet(nvalues, errors);
+}
+
+int DoubleOptionStorage::processAll(AbstractErrorReporter *errors)
+{
+ if (_bTime)
+ {
+ double factor = hostOptions().globalProperties().timeScaleFactor();
+ ValueList::iterator i;
+ for (i = values().begin(); i != values().end(); ++i)
+ {
+ (*i) *= factor;
+ }
+ }
+ return MyBase::processAll(errors);
+}
+
+/********************************************************************
+ * StringOptionStorage
+ */
+
+StringOptionStorage::StringOptionStorage()
+ : _enumIndexStore(NULL)
+{
+}
+
+int StringOptionStorage::init(const StringOption &settings, Options *options)
+{
+ if (settings._defaultEnumIndex >= 0 && settings._enumValues == NULL)
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Cannot set default enum index without enum values");
+ }
+ if (settings._enumIndexStore != NULL && settings._enumValues == NULL)
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Cannot set enum index store without enum values");
+ }
+ if (settings._enumIndexStore != NULL && settings._maxValueCount < 0)
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Cannot set enum index store with arbitrary number of values");
+ }
+ if (settings._enumValues != NULL)
+ {
+ _enumIndexStore = settings._enumIndexStore;
+ const std::string *defaultValue = settings.defaultValue();
+ int match = -1;
+ for (int i = 0; settings._enumValues[i] != NULL; ++i)
+ {
+ if (defaultValue && settings._enumValues[i] == *defaultValue)
+ {
+ match = i;
+ }
+ _allowed.push_back(settings._enumValues[i]);
+ }
+ if (defaultValue)
+ {
+ if (match < 0)
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Default value is not one of allowed values");
+ }
+ }
+ if (settings._defaultEnumIndex >= 0)
+ {
+ if (settings._defaultEnumIndex >= static_cast<int>(_allowed.size()))
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Default enumeration index is out of range");
+ }
+ if (defaultValue && *defaultValue != _allowed[settings._defaultEnumIndex])
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Conflicting default values");
+ }
+ }
+ // If there is no default value, match is still -1.
+ if (_enumIndexStore != NULL)
+ {
+ *_enumIndexStore = match;
+ }
+ }
+ int rc = MyBase::init(settings, options);
+ if (rc == 0)
+ {
+ if (settings._defaultEnumIndex >= 0)
+ {
+ clear();
+ addValue(_allowed[settings._defaultEnumIndex]);
+ if (_enumIndexStore != NULL)
+ {
+ *_enumIndexStore = settings._defaultEnumIndex;
+ }
+ processValues(1, false);
+ }
+ }
+ return rc;
+}
+
+std::string StringOptionStorage::formatValue(int i) const
+{
+ return values()[i];
+}
+
+int StringOptionStorage::convertValue(const std::string &value,
+ AbstractErrorReporter *errors)
+{
+ if (_allowed.size() == 0)
+ {
+ addValue(value);
+ }
+ else
+ {
+ ValueList::const_iterator i;
+ ValueList::const_iterator match = _allowed.end();
+ for (i = _allowed.begin(); i != _allowed.end(); ++i)
+ {
+ // TODO: Case independence.
+ if (i->find(value) == 0)
+ {
+ if (match == _allowed.end() || i->size() < match->size())
+ {
+ match = i;
+ }
+ }
+ }
+ if (match == _allowed.end())
+ {
+ errors->error("Invalid value: " + value);
+ return eeInvalidInput;
+ }
+ addValue(*match);
+ if (_enumIndexStore)
+ {
+ _enumIndexStore[valueCount() - 1] = (match - _allowed.begin());
+ }
+ }
+ return 0;
+}
+
+/********************************************************************
+ * FileNameOptionStorage
+ */
+
+FileNameOptionStorage::FileNameOptionStorage()
+ : _filetype(eftUnknown)
+{
+}
+
+int FileNameOptionStorage::init(const FileNameOption &settings, Options *options)
+{
+ _filetype = settings._filetype;
+ if (_filetype == eftPlot)
+ {
+ options->globalProperties().request(eogpPlotFormat);
+ }
+ return MyBase::init(settings, options);
+}
+
+std::string FileNameOptionStorage::formatValue(int i) const
+{
+ return values()[i];
+}
+
+int FileNameOptionStorage::convertValue(const std::string &value,
+ AbstractErrorReporter * /*errors*/)
+{
+ // TODO: Proper implementation.
+ addValue(value);
+ return 0;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares storage classes for basic option types.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_BASICOPTIONSTORAGE_H
+#define GMX_OPTIONS_BASICOPTIONSTORAGE_H
+
+#include <string>
+#include <vector>
+
+#include "optionfiletype.h"
+#include "optionstoragetemplate.h"
+
+namespace gmx
+{
+
+class IntegerOption;
+class DoubleOption;
+class StringOption;
+class FileNameOption;
+
+/*! \addtogroup module_options
+ * \{
+ */
+
+/*! \internal \brief
+ * Converts, validates, and stores boolean values.
+ */
+class BooleanOptionStorage : public OptionStorageTemplate<bool>
+{
+ public:
+ virtual const char *typeString() const { return "bool"; }
+ virtual std::string formatValue(int i) const;
+
+ private:
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors);
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores integer values.
+ */
+class IntegerOptionStorage : public OptionStorageTemplate<int>
+{
+ public:
+ IntegerOptionStorage();
+
+ virtual const char *typeString() const
+ { return hasFlag(efVector) ? "vector" : "int"; }
+ virtual std::string formatValue(int i) const;
+
+ private:
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors);
+ virtual int processSet(int nvalues, AbstractErrorReporter *errors);
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores floating-point (double) values.
+ */
+class DoubleOptionStorage : public OptionStorageTemplate<double>
+{
+ public:
+ DoubleOptionStorage();
+
+ /*! \brief
+ * Initializes the storage from option settings.
+ *
+ * \param[in] settings Storage settings.
+ * \param[in] options Options object.
+ * \retval 0 on success.
+ */
+ int init(const DoubleOption &settings, Options *options);
+
+ virtual const char *typeString() const;
+ virtual std::string formatValue(int i) const;
+
+ private:
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors);
+ virtual int processSet(int nvalues, AbstractErrorReporter *errors);
+ virtual int processAll(AbstractErrorReporter *errors);
+
+ bool _bTime;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores string values.
+ */
+class StringOptionStorage : public OptionStorageTemplate<std::string>
+{
+ public:
+ StringOptionStorage();
+
+ //! \copydoc DoubleOptionStorage::init()
+ int init(const StringOption &settings, Options *options);
+
+ virtual const char *typeString() const { return _allowed.empty() ? "string" : "enum"; }
+ virtual std::string formatValue(int i) const;
+
+ private:
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors);
+
+ ValueList _allowed;
+ int *_enumIndexStore;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores file names.
+ */
+class FileNameOptionStorage : public OptionStorageTemplate<std::string>
+{
+ public:
+ FileNameOptionStorage();
+
+ //! \copydoc StringOptionStorage::init()
+ int init(const FileNameOption &settings, Options *options);
+
+ virtual const char *typeString() const { return "file"; }
+ virtual std::string formatValue(int i) const;
+
+ private:
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors);
+
+ OptionFileType _filetype;
+};
+
+/*!\}*/
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::CommandLineParser.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_CMDLINEPARSER_IMPL_H
+#define GMX_OPTIONS_CMDLINEPARSER_IMPL_H
+
+#include "cmdlineparser.h"
+#include "optionsassigner.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Private implementation class for CommandLineParser.
+ *
+ * \ingroup module_options
+ */
+class CommandLineParser::Impl
+{
+ public:
+ //! Sets the options object to parse to.
+ Impl(Options *options, AbstractErrorReporter *errors);
+
+ //! Helper object for assigning the options.
+ OptionsAssigner _assigner;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::CommandLineParser.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/cmdlineparser.h"
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/options/optionsassigner.h"
+
+#include "cmdlineparser-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * CommandLineParser::Impl
+ */
+
+CommandLineParser::Impl::Impl(Options *options, AbstractErrorReporter *errors)
+ : _assigner(options, errors)
+{
+ _assigner.setAcceptBooleanNoPrefix(true);
+ _assigner.setNoStrictSectioning(true);
+}
+
+/********************************************************************
+ * CommandLineParser
+ */
+
+CommandLineParser::CommandLineParser(Options *options,
+ AbstractErrorReporter *errors)
+ : _impl(new Impl(options, errors))
+{
+}
+
+CommandLineParser::~CommandLineParser()
+{
+ delete _impl;
+}
+
+int CommandLineParser::parse(int *argc, char *argv[])
+{
+ AbstractErrorReporter *errors = _impl->_assigner.errorReporter();
+ int i = 1;
+ // Start in the discard phase to skip options that can't be understood.
+ bool bDiscard = true;
+
+ _impl->_assigner.start();
+ while (i < *argc)
+ {
+ // Lone '-' is passed as a value.
+ if (argv[i][0] == '-' && argv[i][1] != '\0')
+ {
+ if (!bDiscard)
+ {
+ _impl->_assigner.finishOption();
+ errors->finishContext();
+ }
+ errors->startContext("In command-line option " + std::string(argv[i]));
+ const char *name = &argv[i][1];
+ int rc = _impl->_assigner.startOption(name);
+ bDiscard = (rc != 0);
+ if (bDiscard)
+ {
+ errors->finishContext();
+ }
+ }
+ else if (!bDiscard)
+ {
+ _impl->_assigner.appendValue(argv[i]);
+ }
+ ++i;
+ }
+ if (!bDiscard)
+ {
+ _impl->_assigner.finishOption();
+ errors->finishContext();
+ }
+ return _impl->_assigner.finish();
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::CommandLineParser.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_CMDLINEPARSER_H
+#define GMX_OPTIONS_CMDLINEPARSER_H
+
+namespace gmx
+{
+
+class AbstractErrorReporter;
+
+class Options;
+
+/*! \brief
+ * Implements command-line parsing for Options objects.
+ *
+ * Typical usage (without error checking):
+ * \code
+gmx::Options options("name", "description");
+// Fill up options
+
+gmx::StandardErrorReporter errors;
+gmx::CommandLineParser(&options, &errors).parse(&argc, argv);
+options.finish(&errors);
+ * \endcode
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class CommandLineParser
+{
+ public:
+ /*! \brief
+ * Creates a command-line parser that sets values for options.
+ *
+ * \param[in] options Options object whose options should be set.
+ * \param[in] errors Error reporter object.
+ */
+ CommandLineParser(Options *options, AbstractErrorReporter *errors);
+ ~CommandLineParser();
+
+ /*! \brief
+ * Parses the command-line.
+ *
+ * \retval 0 if there were no errors.
+ */
+ int parse(int *argc, char *argv[]);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ CommandLineParser(const CommandLineParser &);
+ void operator =(const CommandLineParser &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::OptionsGlobalProperties.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/globalproperties.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <smalloc.h>
+#include <statutil.h>
+
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
+
+namespace gmx
+{
+
+static const char *const timeUnits[] = {
+ "fs", "ps", "ns", "us", "ms", "s", NULL
+};
+static const char *const plotFormats[] = {
+ "none", "xmgrace", "xmgr", NULL
+};
+static const double timeScaleFactors[] = {
+ 1e-3, 1, 1e3, 1e6, 1e9, 1e12
+};
+
+
+OptionsGlobalProperties::OptionsGlobalProperties()
+ : _usedProperties(0), _timeUnit(1), _plotFormat(1),
+ _selectionCollection(NULL), _oenv(NULL)
+{
+ snew(_oenv, 1);
+ output_env_init_default(_oenv);
+}
+
+
+OptionsGlobalProperties::~OptionsGlobalProperties()
+{
+ if (_oenv != NULL)
+ {
+ output_env_done(_oenv);
+ }
+}
+
+
+double OptionsGlobalProperties::timeScaleFactor() const
+{
+ assert(_timeUnit >= 0
+ && (size_t)_timeUnit < sizeof(timeScaleFactors)/sizeof(timeScaleFactors[0]));
+ return timeScaleFactors[_timeUnit];
+}
+
+
+void OptionsGlobalProperties::addDefaultOptions(Options *options)
+{
+ if (isPropertyUsed(eogpTimeScaleFactor))
+ {
+ options->addOption(StringOption("tu").enumValue(timeUnits)
+ .defaultValue("ps")
+ .storeEnumIndex(&_timeUnit)
+ .description("Unit for time values"));
+ }
+ if (isPropertyUsed(eogpPlotFormat))
+ {
+ options->addOption(StringOption("xvg").enumValue(plotFormats)
+ .defaultValue("xmgrace")
+ .storeEnumIndex(&_plotFormat)
+ .description("Plot formatting"));
+ }
+}
+
+
+void OptionsGlobalProperties::finish()
+{
+ if (isPropertyUsed(eogpTimeScaleFactor))
+ {
+ _oenv->time_unit = static_cast<time_unit_t>(_timeUnit + 1);
+ }
+ if (isPropertyUsed(eogpPlotFormat))
+ {
+ if (_plotFormat == 0)
+ {
+ _oenv->xvg_format = exvgNONE;
+ }
+ else
+ {
+ _oenv->xvg_format = static_cast<xvg_format_t>(_plotFormat);
+ }
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionsGlobalProperties.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_GLOBALPROPERTIES_H
+#define GMX_OPTIONS_GLOBALPROPERTIES_H
+
+#include <typedefs.h>
+
+namespace gmx
+{
+
+class Options;
+class SelectionCollection;
+
+/*! \libinternal \brief
+ * ID numbers for global properties.
+ */
+enum OptionGlobalPropertyId
+{
+ eogpTimeScaleFactor,
+ eogpPlotFormat,
+ eogpSelectionCollection,
+};
+
+/*! \libinternal \brief
+ * Describes global properties of an Options collection.
+ *
+ * These properties are used to implement features that require all options of
+ * a certain type to access some global data.
+ * For example, if there are options that specify times, and in addition an
+ * option that specifies the unit for these times, all the time options need to
+ * know the scaling factor to get the time in internal units.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsGlobalProperties
+{
+ public:
+ //! Request for a global property to be used.
+ void request(OptionGlobalPropertyId id)
+ {
+ _usedProperties |= (1<<id);
+ }
+
+ //! Set the selection collection for selection option output.
+ void setSelectionCollection(SelectionCollection *sc)
+ {
+ _selectionCollection = sc;
+ }
+
+ //! Returns the scaling factor to get times in ps.
+ double timeScaleFactor() const;
+ //! Returns the selection collection.
+ SelectionCollection *selectionCollection() const
+ {
+ return _selectionCollection;
+ }
+ /*! \brief
+ * Returns an output environment structure for interfacing with old
+ * code.
+ *
+ * Currently, the returned structure is always filled with default
+ * values.
+ *
+ * \deprecated
+ */
+ output_env_t output_env() const
+ {
+ return _oenv;
+ }
+
+ private:
+ OptionsGlobalProperties();
+ ~OptionsGlobalProperties();
+
+ //! Returns true if request() has been called for the given property.
+ bool isPropertyUsed(OptionGlobalPropertyId id) const
+ {
+ return _usedProperties & (1<<id);
+ }
+ void addDefaultOptions(Options *options);
+ /*! \brief
+ * Initializes variables dependent on global properties.
+ *
+ * This method should be called after the values for the options
+ * generated with addDefaultOptions() have been set.
+ */
+ void finish();
+
+ unsigned long _usedProperties;
+ int _timeUnit;
+ int _plotFormat;
+ SelectionCollection *_selectionCollection;
+ output_env_t _oenv;
+
+ friend class Options;
+
+ // Disallow copy and assign.
+ OptionsGlobalProperties(const OptionsGlobalProperties &);
+ void operator =(const OptionsGlobalProperties &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Defines an enumeration type for specifying file types for options.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONFILETYPE_HPP
+#define GMX_OPTIONS_OPTIONFILETYPE_HPP
+
+namespace gmx
+{
+
+/*! \brief
+ * Purpose of file(s) provided through an option.
+ *
+ * \ingroup module_options
+ */
+enum OptionFileType {
+ eftUnknown,
+ eftTopology,
+ eftRunInput,
+ eftTrajectory,
+ eftIndex,
+ eftPlot,
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Defines flags used in option implementation.
+ *
+ * Symbols in this header are considered an implementation detail, and should
+ * not be accessed outside the module.
+ * Because of details in the implementation, it is still installed.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONFLAGS_H
+#define GMX_OPTIONS_OPTIONFLAGS_H
+
+#include "../utility/flags.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Flags for options.
+ *
+ * These flags are not part of the public interface, even though they are in an
+ * installed header. They are needed in a few template class implementations.
+ */
+enum OptionFlag
+{
+ //! %Option has been set.
+ efSet = 1<<0,
+ /*! \brief
+ * The current value of the option is a default value.
+ *
+ * This flag is also set when a new option source starts, such that values
+ * from the new source will overwrite old ones.
+ */
+ efHasDefaultValue = 1<<1,
+ //! %Option is required to be set.
+ efRequired = 1<<2,
+ //! %Option can be specified multiple times.
+ efMulti = 1<<3,
+ //! %Option is hidden from standard help.
+ efHidden = 1<<4,
+ /*! \brief
+ * %Option provides a boolean value.
+ *
+ * This is used to optionally support an alternative syntax where an
+ * option provided with no value sets the value to true and an
+ * option prefixed with "no" clears the value.
+ */
+ efBoolean = 1<<5,
+ /*! \brief
+ * %Option value is a vector, but a single value is also accepted.
+ *
+ * If only a single value is provided, the storage object should fill the
+ * whole vector with that value. The length of the vector must be fixed.
+ * The default length is 3 elements.
+ */
+ efVector = 1<<6,
+ efExternalStore = 1<<8,
+ efExternalStoreArray = 1<<9,
+ efExternalValueVector = 1<<10,
+ //! %Option does not support default values.
+ efNoDefaultValue = 1<<7,
+ /*! \brief
+ * Storage object may add zero values even when a value is provided.
+ *
+ * In order to do proper error checking, this flag should be set when it is
+ * possible that the AbstractOptionStorage::appendValue() method of the
+ * storage object does not add any values for the option and still
+ * succeeds.
+ */
+ efConversionMayNotAddValues = 1<<11,
+ /*! \brief
+ * Storage object does its custom checking for minimum value count.
+ *
+ * If this flag is set, the class derived from AbstractOptionStorage should
+ * implement processSet(), processAll(), and possible other functions it
+ * provides such that it always fails if not enough values are provided.
+ * This is useful to override the default check, which is done in
+ * AbstractOptionStorage::processSet().
+ */
+ efDontCheckMinimumCount = 1<<16,
+ efFile = 1<<12,
+ efFileRead = 1<<13,
+ efFileWrite = 1<<14,
+ efFileLibrary = 1<<15,
+ //efDynamic = 1<<16,
+ //efRanges = 1<<17,
+ //efEnum = 1<<18,
+ //efStaticEnum = 1<<19,
+ //efVarNum = 1<<20,
+ //efAtomVal = 1<<21,
+};
+
+//! Holds a combination of ::OptionFlag values.
+typedef FlagsTemplate<OptionFlag> OptionFlags;
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::Options.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONS_IMPL_H
+#define GMX_OPTIONS_OPTIONS_IMPL_H
+
+#include <string>
+#include <vector>
+
+#include "options.h"
+
+namespace gmx
+{
+
+class AbstractOptionStorage;
+class OptionsGlobalProperties;
+
+/*! \internal \brief
+ * Private implementation class for Options.
+ *
+ * Note that in addition to Options, the OptionsAssigner and OptionsIterator
+ * classes also directly access this class.
+ *
+ * \ingroup module_options
+ */
+class Options::Impl
+{
+ public:
+ //! Convenience type for list of sections.
+ typedef std::vector<Options *> SubSectionList;
+ //! Convenience type for list of options.
+ typedef std::vector<AbstractOptionStorage *> OptionList;
+
+ //! Sets the name and title.
+ Impl(const char *name, const char *title);
+ ~Impl();
+
+ /*! \brief
+ * Finds a subsection by name.
+ *
+ * \param[in] name Name to search for.
+ * \returns Pointer to the found subsection, or NULL if not found.
+ */
+ Options *findSubSection(const char *name) const;
+ /*! \brief
+ * Finds an option by name.
+ *
+ * \param[in] name Name to search for.
+ * \returns Pointer to the found option, or NULL if not found.
+ */
+ AbstractOptionStorage *findOption(const char *name) const;
+
+ /*! \brief
+ * Calls Option::startSource() for all options, including subsections.
+ *
+ * \returns 0 on success, or the first non-zero return value of the
+ * called functions.
+ */
+ int startSource();
+
+ //! Name for the Options object.
+ std::string _name;
+ //! Description title for the Options object.
+ std::string _title;
+ //! Full description for the Options object.
+ std::string _description;
+ /*! \brief
+ * List of subsections, in insertion order.
+ *
+ * This container contains only references to external objects; memory
+ * management is performed elsewhere.
+ */
+ SubSectionList _subSections;
+ /*! \brief
+ * List of options, in insertion order.
+ *
+ * All objects in this container are owned by this object, and are
+ * freed in the destructor.
+ */
+ OptionList _options;
+ //! Options object that contains this object as a subsection, or NULL.
+ Options *_parent;
+ /*! \brief
+ * Object that contains global properties, or NULL if \a _parent != NULL.
+ *
+ * This object is always owned by the Options object.
+ * For subsections, the global properties are kept in the parent, and
+ * this pointer is NULL.
+ */
+ OptionsGlobalProperties *_globalProperties;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::Options.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/options.h"
+
+#include <cassert>
+#include <cctype>
+#include <cstring>
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/options/abstractoptionstorage.h"
+#include "gromacs/options/globalproperties.h"
+
+#include "options-impl.h"
+
+namespace gmx
+{
+
+static std::string composeString(const char *const *sarray)
+{
+ std::string result;
+
+ for (int i = 0; sarray[i] != NULL; ++i)
+ {
+ if (sarray[i][0] != '\0')
+ {
+ result.append(sarray[i]);
+ char lastchar = sarray[i][std::strlen(sarray[i])-1];
+ if (!std::isspace(lastchar))
+ {
+ result.append(" ");
+ }
+ }
+ }
+ result.resize(result.find_last_not_of(" \n\r\t") + 1);
+ return result;
+}
+
+/********************************************************************
+ * Options::Impl
+ */
+
+Options::Impl::Impl(const char *name, const char *title)
+ : _name(name != NULL ? name : ""), _title(title != NULL ? title : ""),
+ _parent(NULL), _globalProperties(new OptionsGlobalProperties)
+{
+}
+
+Options::Impl::~Impl()
+{
+ OptionList::const_iterator i;
+ for (i = _options.begin(); i != _options.end(); ++i)
+ {
+ delete *i;
+ }
+ delete _globalProperties;
+}
+
+Options *Options::Impl::findSubSection(const char *name) const
+{
+ SubSectionList::const_iterator i;
+ for (i = _subSections.begin(); i != _subSections.end(); ++i)
+ {
+ if ((*i)->name() == name)
+ {
+ return *i;
+ }
+ }
+ return NULL;
+}
+
+AbstractOptionStorage *Options::Impl::findOption(const char *name) const
+{
+ OptionList::const_iterator i;
+ for (i = _options.begin(); i != _options.end(); ++i)
+ {
+ if ((*i)->name() == name)
+ {
+ return *i;
+ }
+ }
+ return NULL;
+}
+
+int Options::Impl::startSource()
+{
+ int rc = 0;
+ OptionList::const_iterator i;
+ for (i = _options.begin(); i != _options.end(); ++i)
+ {
+ AbstractOptionStorage *option = *i;
+ int rc1 = option->startSource();
+ rc = (rc != 0 ? rc : rc1);
+ }
+ SubSectionList::const_iterator j;
+ for (j = _subSections.begin(); j != _subSections.end(); ++j)
+ {
+ Options *section = *j;
+ int rc1 = section->_impl->startSource();
+ rc = (rc != 0 ? rc : rc1);
+ }
+ return rc;
+}
+
+/********************************************************************
+ * Options
+ */
+
+Options::Options(const char *name, const char *title)
+ : _impl(new Impl(name, title))
+{
+}
+
+Options::~Options()
+{
+ delete _impl;
+}
+
+const std::string &Options::name() const
+{
+ return _impl->_name;
+}
+
+const std::string &Options::title() const
+{
+ return _impl->_title;
+}
+
+const std::string &Options::description() const
+{
+ return _impl->_description;
+}
+
+void Options::setDescription(const char *const *desc)
+{
+ _impl->_description = composeString(desc);
+}
+
+void Options::addSubSection(Options *section)
+{
+ // Make sure that section is not already inserted somewhere.
+ assert(section->_impl->_parent == NULL);
+ // Make sure that there are no duplicate sections.
+ assert(_impl->findSubSection(section->name().c_str()) == NULL);
+ _impl->_subSections.push_back(section);
+ section->_impl->_parent = this;
+
+ globalProperties()._usedProperties |=
+ section->_impl->_globalProperties->_usedProperties;
+ delete section->_impl->_globalProperties;
+ section->_impl->_globalProperties = NULL;
+}
+
+void Options::addOption(const AbstractOption &settings)
+{
+ AbstractOptionStorage *option = NULL;
+ int rc = settings.createDefaultStorage(this, &option);
+ // Caller code should be fixed if option initialization fails.
+ assert(rc == 0);
+ // Make sure that there are no duplicate options.
+ assert(_impl->findOption(option->name().c_str()) == NULL);
+ _impl->_options.push_back(option);
+}
+
+void Options::addDefaultOptions()
+{
+ globalProperties().addDefaultOptions(this);
+}
+
+bool Options::isSet(const char *name) const
+{
+ AbstractOptionStorage *option = _impl->findOption(name);
+ return (option != NULL ? option->isSet() : false);
+}
+
+int Options::finish(AbstractErrorReporter *errors)
+{
+ int rc = 0;
+ Impl::OptionList::const_iterator i;
+ for (i = _impl->_options.begin(); i != _impl->_options.end(); ++i)
+ {
+ AbstractOptionStorage *option = *i;
+ int rc1 = option->finish(errors);
+ rc = (rc != 0 ? rc : rc1);
+ }
+ Impl::SubSectionList::const_iterator j;
+ for (j = _impl->_subSections.begin(); j != _impl->_subSections.end(); ++j)
+ {
+ Options *section = *j;
+ int rc1 = section->finish(errors);
+ rc = (rc != 0 ? rc : rc1);
+ }
+ if (_impl->_parent == NULL)
+ {
+ assert(_impl->_globalProperties != NULL);
+ _impl->_globalProperties->finish();
+ }
+ return rc;
+}
+
+OptionsGlobalProperties &Options::globalProperties()
+{
+ Options *section = this;
+ while (section->_impl->_parent != NULL)
+ {
+ section = section->_impl->_parent;
+ }
+ return *section->_impl->_globalProperties;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::Options.
+ *
+ * Together with basicoptions.h, this header forms the part of the public
+ * API that most classes will use to provide options.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONS_H
+#define GMX_OPTIONS_OPTIONS_H
+
+#include <string>
+
+namespace gmx
+{
+
+class AbstractErrorReporter;
+
+class AbstractOption;
+class OptionsGlobalProperties;
+class OptionsAssigner;
+class OptionsIterator;
+
+/*! \brief
+ * Collection of options.
+ *
+ * This class provides a standard interface for implementing input options.
+ * Standard usage is to write a method that creates an Options that is owned by
+ * the object, populates it with supported options, and then returns it:
+ * \code
+// <as class attributes>
+using gmx::Options;
+Options options("common", "Common Options");
+std::string arg1;
+int arg2;
+
+// <populating>
+using gmx::StringOption;
+using gmx::IntegerOption;
+options.addOption(StringOption("arg1").store(&arg1));
+options.addOption(IntegerOption("arg2").store(&arg2));
+return &options;
+ * \endcode
+ * The caller of that method can then use a parser implementation such as
+ * CommandLineParser to provide values for the options.
+ *
+ * Header basicoptions.h provides declarations of several standard
+ * option types for use with addOption(). Documentation of those classes
+ * also give more examples of how to define options.
+ *
+ * In order to keep the public interface of this class simple and to reduce
+ * build dependencies on objects that simply provide options, functionality
+ * to assign values to options is provided by a separate OptionsAssigner class.
+ * Similarly, functionality for looping over all options (e.g., for writing out
+ * help) is provided by OptionsIterator.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class Options
+{
+ public:
+ /*! \brief
+ * Initializes the name and title of an option collection.
+ *
+ * \param[in] name Single-word name.
+ * \param[in] title Descriptive title.
+ *
+ * Copies the input strings.
+ */
+ Options(const char *name, const char *title);
+ ~Options();
+
+ //! Returns the short name of the option collection.
+ const std::string &name() const;
+ //! Returns the title of the option collection.
+ const std::string &title() const;
+ //! Returns the full description of the option collection.
+ const std::string &description() const;
+
+ //! Sets the full description of the option collection.
+ void setDescription(const char *const *desc);
+ //int addBugs(int nbugs, const char *const *bugs);
+
+ /*! \brief
+ * Adds an option collection as a subsection of this collection.
+ *
+ * The name() field of \p section is used as the name of the
+ * subsection.
+ *
+ * For certain functionality to work properly, no options should
+ * be added to the subsection after it has been added to another
+ * collection.
+ *
+ * Only a pointer to the provided object is stored. The caller is
+ * responsible that the object exists for the lifetime of the
+ * collection.
+ * It is not possible to add the same Options object as a subsection to
+ * several different Options.
+ */
+ void addSubSection(Options *section);
+ /*! \brief
+ * Adds a recognized option to the collection.
+ *
+ * The behavior is undefined if invalid settings are provided.
+ * The current implementation asserts if it detects an error.
+ *
+ * See \link Options class documentation \endlink for example usage.
+ */
+ void addOption(const AbstractOption &settings);
+ /*! \brief
+ * Adds default options to this collection.
+ *
+ * Adds default options for altering global properties such as the time
+ * unit into this collection.
+ *
+ * It is possible to call this method on a subsection of a collection.
+ * Even in that case, the generated options set the properties for the
+ * parent collection.
+ */
+ void addDefaultOptions();
+
+ //! Returns true if option is set.
+ bool isSet(const char *name) const;
+ /*! \brief
+ * Notifies the collection that all option values are assigned.
+ *
+ * \param[in] errors Error reporter for reporting errors.
+ * \retval 0 on success.
+ *
+ * This function should be called after no more option values are
+ * to be assigned. Values in storage variables are guaranteed to be
+ * available only after this call.
+ */
+ int finish(AbstractErrorReporter *errors);
+
+ /*! \brief
+ * Returns the global property object for this collection.
+ *
+ * The caller should not store pointers or references to the object;
+ * it can change if this object is added as a subsection into
+ * another collection.
+ *
+ * \see OptionsGlobalProperties
+ */
+ OptionsGlobalProperties &globalProperties();
+ //! \copydoc globalProperties()
+ const OptionsGlobalProperties &globalProperties() const
+ { return const_cast<Options *>(this)->globalProperties(); }
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ friend class Impl;
+
+ //! Needed to be able to extend the interface of this object.
+ friend class OptionsAssigner;
+ //! Needed to be able to extend the interface of this object.
+ friend class OptionsIterator;
+
+ // Disallow copy and assign.
+ Options(const Options &);
+ void operator =(const Options &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::OptionsAssigner.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSASSIGNER_IMPL_H
+#define GMX_OPTIONS_OPTIONSASSIGNER_IMPL_H
+
+#include <vector>
+
+#include "optionsassigner.h"
+
+namespace gmx
+{
+
+class AbstractErrorReporter;
+
+class AbstractOptionStorage;
+class Options;
+
+/*! \internal \brief
+ * Private implementation class for OptionsAssigner.
+ *
+ * \ingroup module_options
+ */
+class OptionsAssigner::Impl
+{
+ public:
+ //! Possible flags for controlling assignment behavior.
+ enum Flag
+ {
+ //! Recognize boolean option "name" also as "noname".
+ efAcceptBooleanNoPrefix = 1<<0,
+ //! Look for options in all sections, not just the current one.
+ efNoStrictSectioning = 1<<1,
+ };
+ //! Sets the option object to assign to.
+ Impl(Options *options, AbstractErrorReporter *errors);
+
+ /*! \brief
+ * Stores error code for later retrieval if it is the first error.
+ *
+ * \param[in] code Error code to store.
+ * \returns \p code
+ *
+ * The first call with a non-zero \p code sets the \p _errorCode
+ * attribute; later calls do nothing The return value allows the
+ * function to be used like \c "return keepError(rc);"
+ */
+ int keepError(int code)
+ {
+ if (_errorCode == 0)
+ {
+ _errorCode = code;
+ }
+ return code;
+ }
+
+ //! Sets or clears the given flag.
+ void setFlag(Flag flag, bool bSet);
+
+ //! Returns true if the given flag is set.
+ bool hasFlag(Flag flag) const { return _flags & flag; }
+ //! Returns true if a subsection has been set.
+ bool inSubSection() const { return _sectionStack.size() > 1; }
+ //! Returns the Options object for the current section.
+ Options ¤tSection() const { return *_sectionStack.back(); }
+ /*! \brief
+ * Finds an option by the given name.
+ *
+ * \param[in] name Name of the option to look for.
+ * \returns Pointer to the found option, or NULL if none found.
+ *
+ * This function takes into account the flags specified, and may change
+ * the internal state of the assigner to match the option found.
+ * If no option is found, the internal state is not modified.
+ */
+ AbstractOptionStorage *findOption(const char *name);
+
+ //! Options object to assign to.
+ Options &_options;
+ //! Error reporter to use for errors.
+ AbstractErrorReporter *_errors;
+ //! Flags that control assignment behavior.
+ unsigned long _flags;
+ /*! \brief
+ * List of (sub)sections being assigned to.
+ *
+ * The first element always points to \a _options.
+ */
+ std::vector<Options *> _sectionStack;
+ //! Current option being assigned to, or NULL if none.
+ AbstractOptionStorage *_currentOption;
+ //! Keeps the error code of the first error encountered, or 0 if none.
+ int _errorCode;
+ //! Number of values assigned so far to the current option.
+ int _currentValueCount;
+ //! If true, a "no" prefix was given for the current boolean option.
+ bool _reverseBoolean;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::OptionsAssigner.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/optionsassigner.h"
+
+#include <deque>
+
+#include <cassert>
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/abstractoptionstorage.h"
+#include "gromacs/options/options.h"
+
+#include "optionsassigner-impl.h"
+#include "options-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * OptionsAssigner::Impl
+ */
+
+OptionsAssigner::Impl::Impl(Options *options, AbstractErrorReporter *errors)
+ : _options(*options), _errors(errors), _flags(0), _currentOption(NULL),
+ _errorCode(0), _currentValueCount(0), _reverseBoolean(false)
+{
+ _sectionStack.push_back(&_options);
+}
+
+void OptionsAssigner::Impl::setFlag(OptionsAssigner::Impl::Flag flag, bool bSet)
+{
+ if (bSet)
+ {
+ _flags |= flag;
+ }
+ else
+ {
+ _flags &= ~flag;
+ }
+}
+
+AbstractOptionStorage *
+OptionsAssigner::Impl::findOption(const char *name)
+{
+ assert(_currentOption == NULL);
+ AbstractOptionStorage *option = NULL;
+ Options *section = NULL;
+ Options *root = ¤tSection();
+ Options *oldRoot = NULL;
+ int upcount = 0;
+ std::deque<Options *> searchList;
+ searchList.push_back(root);
+ while (option == NULL && !searchList.empty())
+ {
+ section = searchList.front();
+ option = section->_impl->findOption(name);
+ if (option == NULL && hasFlag(efAcceptBooleanNoPrefix))
+ {
+ if (name[0] == 'n' && name[1] == 'o')
+ {
+ option = section->_impl->findOption(name + 2);
+ if (option != NULL && option->isBoolean())
+ {
+ _reverseBoolean = true;
+ }
+ else
+ {
+ option = NULL;
+ }
+ }
+ }
+ searchList.pop_front();
+ if (hasFlag(efNoStrictSectioning))
+ {
+ Options::Impl::SubSectionList::const_iterator i;
+ for (i = section->_impl->_subSections.begin();
+ i != section->_impl->_subSections.end(); ++i)
+ {
+ if (*i != oldRoot)
+ {
+ searchList.push_back(*i);
+ }
+ }
+ if (searchList.empty() && root != &_options)
+ {
+ Options *oldRoot = root;
+ root = root->_impl->_parent;
+ ++upcount;
+ searchList.push_back(root);
+ }
+ }
+ }
+ if (hasFlag(efNoStrictSectioning) && option != NULL)
+ {
+ while (upcount > 0)
+ {
+ _sectionStack.pop_back();
+ --upcount;
+ }
+ std::vector<Options *> sections;
+ while (section != ¤tSection())
+ {
+ sections.push_back(section);
+ section = section->_impl->_parent;
+ }
+ while (!sections.empty())
+ {
+ _sectionStack.push_back(sections.back());
+ sections.pop_back();
+ }
+ }
+ return option;
+}
+
+/********************************************************************
+ * OptionsAssigner
+ */
+
+OptionsAssigner::OptionsAssigner(Options *options, AbstractErrorReporter *errors)
+ : _impl(new Impl(options, errors))
+{
+}
+
+OptionsAssigner::~OptionsAssigner()
+{
+ delete _impl;
+}
+
+AbstractErrorReporter *OptionsAssigner::errorReporter() const
+{
+ return _impl->_errors;
+}
+
+void OptionsAssigner::setAcceptBooleanNoPrefix(bool enabled)
+{
+ _impl->setFlag(Impl::efAcceptBooleanNoPrefix, enabled);
+}
+
+void OptionsAssigner::setNoStrictSectioning(bool enabled)
+{
+ _impl->setFlag(Impl::efNoStrictSectioning, enabled);
+}
+
+int OptionsAssigner::start()
+{
+ return _impl->keepError(_impl->_options._impl->startSource());
+}
+
+int OptionsAssigner::startSubSection(const char *name)
+{
+ if (_impl->_currentOption != NULL)
+ {
+ // The return code is ignored to keep on assigning, but any error is
+ // stored to be returned in finish().
+ finishOption();
+ }
+
+ Options *section = _impl->currentSection()._impl->findSubSection(name);
+ if (section == NULL)
+ {
+ // TODO: Print an error
+ return _impl->keepError(eeInvalidInput);
+ }
+ _impl->_sectionStack.push_back(section);
+ return 0;
+}
+
+int OptionsAssigner::startOption(const char *name)
+{
+ if (_impl->_currentOption != NULL)
+ {
+ // The return code is ignored to keep on assigning, but any error is
+ // stored to be returned in finish().
+ finishOption();
+ }
+
+ AbstractOptionStorage *option = _impl->findOption(name);
+ if (option == NULL)
+ {
+ _impl->_errors->error("Unknown option");
+ return _impl->keepError(eeInvalidInput);
+ }
+ int rc = option->startSet(_impl->_errors);
+ if (rc != 0)
+ {
+ return _impl->keepError(rc);
+ }
+ _impl->_currentOption = option;
+ _impl->_currentValueCount = 0;
+ return 0;
+}
+
+int OptionsAssigner::appendValue(const std::string &value)
+{
+ AbstractOptionStorage *option = _impl->_currentOption;
+ // The option should have been successfully started.
+ assert(option != NULL);
+ ++_impl->_currentValueCount;
+ return _impl->keepError(option->appendValue(value, _impl->_errors));
+}
+
+int OptionsAssigner::finishOption()
+{
+ AbstractOptionStorage *option = _impl->_currentOption;
+ // The option should have been successfully started.
+ assert(option != NULL);
+ int rc = 0;
+ if (option->isBoolean())
+ {
+ if (_impl->_currentValueCount == 0)
+ {
+ // TODO: Get rid of the hard-coded strings.
+ rc = option->appendValue(_impl->_reverseBoolean ? "0" : "1",
+ _impl->_errors);
+ // If the above fails, there is something wrong.
+ assert(rc == 0);
+ }
+ else if (_impl->_reverseBoolean)
+ {
+ _impl->_errors->error("Cannot specify a value together with 'no' prefix");
+ rc = eeInvalidInput;
+ }
+ }
+ int rc1 = _impl->_currentOption->finishSet(_impl->_errors);
+ rc = (rc != 0 ? rc : rc1);
+ _impl->_currentOption = NULL;
+ _impl->_reverseBoolean = false;
+ return _impl->keepError(rc);
+}
+
+int OptionsAssigner::finishSubSection()
+{
+ // Should only be called if we are in a subsection.
+ assert(_impl->inSubSection());
+ if (_impl->_currentOption != NULL)
+ {
+ // Possible error codes are stored and returned in the end.
+ finishOption();
+ }
+ _impl->_sectionStack.pop_back();
+ return 0;
+}
+
+int OptionsAssigner::finish()
+{
+ if (_impl->_currentOption != NULL)
+ {
+ // Possible error codes are stored and returned in the end.
+ finishOption();
+ }
+ while (_impl->inSubSection())
+ {
+ // Possible error codes are stored and returned in the end.
+ finishSubSection();
+ }
+ return _impl->_errorCode;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionsAssigner.
+ *
+ * This header is only needed when implementing option parsers.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSASSIGNER_H
+#define GMX_OPTIONS_OPTIONSASSIGNER_H
+
+#include <string>
+
+namespace gmx
+{
+
+class AbstractErrorReporter;
+
+class Options;
+
+/*! \libinternal \brief
+ * Decorator class for assigning values to Options.
+ *
+ * This class extends the interface of an Options object by providing methods
+ * to set values for options. It also keeps track of necessary state variables
+ * to assign values to options in subsections within the Options object.
+ * Typical use (without error checking):
+ * \code
+gmx::options::Options options("name", "Title");
+// Set up options
+
+gmx::error::StandardReporter errors;
+gmx::options::OptionsAssigner assigner(&options, &errors);
+assigner.startOption("opt1");
+assigner.appendValue("3");
+assigner.startSubSection("section");
+assigner.startOption("opt2"); // Now in the subsection
+assigner.appendValue("yes");
+assigner.finishSubSection()
+assigner.startOption("opt3"); // Again in the main options
+assigner.appendValue("2");
+assigner.finish(); // At minimum, the return value of finish() should be checked.
+ * \endcode
+ *
+ * As shown in the example, calling finishOption() or finishSubSection() is
+ * optional; they are automatically called when appropriate by startOption(),
+ * startSubSection(), and finish().
+ * However, you need to call them explicitly if you want to act on the return
+ * value: these calls do not influence the return value of
+ * startOption() / startSubSection().
+ * They do influence the return value of finish(), however.
+ * The finish() method should always be called.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsAssigner
+{
+ public:
+ /*! \brief
+ * Creates an object that assigns to the given object.
+ */
+ OptionsAssigner(Options *options, AbstractErrorReporter *errors);
+ ~OptionsAssigner();
+
+ /*! \brief
+ * Returns the error reporter object passed to the constructor.
+ *
+ * This method is provided for convenience such that users of this
+ * class do not need to store a separate pointer to the error reporter
+ * if they need to use it.
+ */
+ AbstractErrorReporter *errorReporter() const;
+
+ /*! \brief
+ * Sets the assigner to recognize boolean options with a "no" prefix.
+ *
+ * With this option set, \c startOption("noname") is interpreted as
+ * \c startOption("name") followed by \c appendValue("no"), if there is
+ * no option by the name "noname", but there is a boolean option with
+ * name "name".
+ *
+ * By default, the prefix is not recognized.
+ *
+ * Can be set or cleared at any time, and will have effect on all
+ * subsequent calls of startOption().
+ */
+ void setAcceptBooleanNoPrefix(bool enabled);
+ /*! \brief
+ * Sets the assigner to find options in non-active sections.
+ *
+ * By default, options are only looked for in the currently active
+ * subsection. With this option set, if no matching option is found in
+ * the current section, a breadth-first search is performed, first on
+ * all subsections of the current section, and then going up one level
+ * at a time. The first matching option is used, and the current
+ * section is changed to the section that contains the matching option.
+ *
+ * Can be set or cleared at any time, and will have effect on all
+ * subsequent calls of startOption().
+ */
+ void setNoStrictSectioning(bool enabled);
+
+ /*! \brief
+ * Start assigning values.
+ *
+ * \retval 0 on success.
+ */
+ int start();
+ /*! \brief
+ * Start assigning values to options in a subsection.
+ *
+ * \param[in] name Name of the subsection to start assigning to.
+ * \retval 0 if assignment can proceed.
+ *
+ * Does not call finishSubSection() automatically to enable nested
+ * sections.
+ */
+ int startSubSection(const char *name);
+ /*! \brief
+ * Start assigning values for an option.
+ *
+ * \param[in] name Name of the option to start assigning to.
+ * \retval 0 if assignment can proceed.
+ */
+ int startOption(const char *name);
+ /*! \brief
+ * Appends a value to the value list of the current option.
+ *
+ * \param[in] value String representation of the value to assign.
+ * \retval 0 if assignment was successful.
+ */
+ int appendValue(const std::string &value);
+ /*! \brief
+ * Finish assigning values for the current option.
+ *
+ * \retval 0 if there were no errors in the assignment.
+ *
+ * This function returns non-zero only if the error could not have been
+ * detected earlier, i.e., from the return value of appendValue().
+ */
+ int finishOption();
+ /*! \brief
+ * Finish assigning values to a subsection.
+ *
+ * \retval 0 for success.
+ *
+ * This function returns non-zero only if the error could not have been
+ * detected earlier.
+ */
+ int finishSubSection();
+ /*! \brief
+ * Finish assigning options through the object.
+ *
+ * \retval 0 if there were no errors in the assignment.
+ *
+ * If an error was detected in any of the other calls to this class,
+ * this function returns the error code of the first of such errors.
+ */
+ int finish();
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ OptionsAssigner(const OptionsAssigner &);
+ void operator =(const OptionsAssigner &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Defines gmx::OptionStorageTemplate template.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
+#define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
+
+#include <cassert>
+
+#include <string>
+#include <vector>
+
+#include "abstractoption.h"
+#include "abstractoptionstorage.h"
+
+namespace gmx
+{
+
+class Options;
+
+/*! \brief
+ * Templated base class for constructing option value storage classes.
+ *
+ * \tparam T Assignable type that stores a single option value.
+ *
+ * Provides an implementation of the clear() and valueCount() methods of
+ * AbstractOptionStorage, as well as a basic implementation of processSet() and
+ * processAll(). This leaves typeString(), formatValue(), and convertValue()
+ * to be implemented in derived classes.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template <typename T>
+class OptionStorageTemplate : public AbstractOptionStorage
+{
+ public:
+ //! Alias for the template class for use in base classes.
+ typedef OptionStorageTemplate<T> MyBase;
+ //! Type of the container that contains the current values.
+ typedef std::vector<T> ValueList;
+
+ virtual ~OptionStorageTemplate();
+
+ /*! \brief
+ * Initializes the storage from option settings.
+ *
+ * \retval 0 on success.
+ *
+ * \see OptionTemplate::createDefaultStorage()
+ */
+ template <class U>
+ int init(const OptionTemplate<T, U> &settings, Options *options);
+
+ // No implementation in this class for the pure virtual methods, but
+ // the declarations are still included for clarity.
+ virtual const char *typeString() const = 0;
+ virtual int valueCount() const { return _values->size(); }
+ virtual std::string formatValue(int i) const = 0;
+
+ protected:
+ //! Initializes default values (no storage).
+ OptionStorageTemplate();
+
+ virtual void clear();
+ /*! \copydoc AbstractOptionStorage::convertValue()
+ *
+ * Derived classes should call addValue() after they have converted
+ * \p value to the storage type.
+ */
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors) = 0;
+ /*! \copydoc AbstractOptionStorage::processSet()
+ *
+ * The implementation in OptionStorageTemplate copies the values
+ * from the main storage vector to alternate locations, and always
+ * succeeds. Derived classes should always call the base class
+ * implementation if they override this method.
+ */
+ virtual int processSet(int nvalues,
+ AbstractErrorReporter * /*errors*/)
+ {
+ processValues(nvalues, true);
+ return 0;
+ }
+ /*! \copydoc AbstractOptionStorage::processAll()
+ *
+ * The implementation in OptionStorageTemplate does nothing, and always
+ * returns zero. Derived classes should still always call the base
+ * class implementation if they override this method.
+ */
+ virtual int processAll(AbstractErrorReporter * /*errors*/)
+ { return 0; }
+
+ /*! \brief
+ * Adds a value to the storage.
+ *
+ * \param[in] value Value to add. A copy is made.
+ * \retval 0 on success.
+ * \retval ::eeInvalidInput if the maximum value count has been reached.
+ *
+ * Derived classes should call this function from the convertValue()
+ * implementation to add converted values to the storage.
+ * It is only necessary to check the return value if addValue() is
+ * called more than once from one convertValue() invocation, or if
+ * ::efConversionMayNotAddValues is specified.
+ */
+ int addValue(const T &value);
+ /*! \brief
+ * Store values in alternate locations.
+ *
+ * \param[in] nvalues Number of values to process.
+ * \param[in] bDoArray Whether to put values in the array storage as
+ * well.
+ *
+ * Stores the last \p nvalues values added with addValue() to the
+ * alternate storage locations.
+ * The current implementation asserts if it is called more than once
+ * with \p bDoArray set to true.
+ *
+ * Derived classes should call this method if they use addValue()
+ * outside convertValue(), e.g., to set a default value. In such
+ * cases, \p bDoArray should be set to false.
+ */
+ void processValues(int nvalues, bool bDoArray);
+
+ //! Provides derived classes access to the current list of values.
+ ValueList &values() { return *_values; }
+ //! Provides derived classes access to the current list of values.
+ const ValueList &values() const { return *_values; }
+
+ private:
+ /*! \brief
+ * Vector for primary storage of option values.
+ *
+ * Is never NULL; points either to externally provided vector, or an
+ * internally allocated one. The allocation is performed by init().
+ *
+ * addValue() adds values only to this storage. Other memory locations
+ * are updated only when processAll() is called.
+ */
+ ValueList *_values;
+ T *_store;
+ T **_storeArray;
+ int *_nvalptr;
+
+ // Copy and assign disallowed by base.
+};
+
+/*! \brief
+ * Helper function for creating storage objects.
+ *
+ * \tparam T Type of the settings object (derived from OptionTemplate).
+ * \tparam S Type of the storage object (derived from OptionStorageTemplate).
+ * \param[in] settings Settings object to pass to S::init().
+ * \param[in] options Options object to pass to S::init().
+ * \param[out] output Pointer to the created storage object.
+ * \returns The return value of S::init().
+ *
+ * Creates a new instance of S and calls the init() method with the provided
+ * parameters. If the initialization fails, destroys the partially constructed
+ * object.
+ *
+ * \inlibraryapi
+ */
+template <class T, class S> int
+createOptionStorage(const T *settings, Options *options,
+ AbstractOptionStorage **output)
+{
+ S *storage = new S;
+ int rc = storage->init(*settings, options);
+ if (rc != 0)
+ {
+ delete storage;
+ return rc;
+ }
+ *output = storage;
+ return 0;
+}
+
+
+template <typename T>
+OptionStorageTemplate<T>::OptionStorageTemplate()
+ : _values(NULL), _store(NULL), _storeArray(NULL), _nvalptr(NULL)
+{
+}
+
+
+template <typename T>
+OptionStorageTemplate<T>::~OptionStorageTemplate()
+{
+ if (!hasFlag(efExternalValueVector))
+ {
+ delete _values;
+ }
+}
+
+
+template <typename T>
+template <class U>
+int OptionStorageTemplate<T>::init(const OptionTemplate<T, U> &settings, Options *options)
+{
+ // It's impossible for the caller to do proper memory management if
+ // the provided memory is not initialized as NULL.
+ assert(settings._storeArray == NULL || *settings._storeArray == NULL);
+ int rc = AbstractOptionStorage::init(settings, options);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ _store = settings._store;
+ _storeArray = settings._storeArray;
+ _nvalptr = settings._nvalptr;
+ _values = settings._storeVector;
+ if (!_values)
+ {
+ // The flag should be set for proper error checking.
+ assert(!hasFlag(efExternalValueVector));
+ _values = new std::vector<T>;
+ }
+ // If the option does not support default values, one should not be set.
+ assert(!hasFlag(efNoDefaultValue) || settings._defaultValue == NULL);
+ if (!hasFlag(efNoDefaultValue))
+ {
+ if (settings._defaultValue != NULL)
+ {
+ _values->clear();
+ addValue(*settings._defaultValue);
+ processValues(1, false);
+ setFlag(efHasDefaultValue);
+ }
+ else if (!hasFlag(efExternalValueVector) && _store != NULL)
+ {
+ _values->clear();
+ int count = (settings.isVector() ?
+ settings._maxValueCount : settings._minValueCount);
+ for (int i = 0; i < count; ++i)
+ {
+ _values->push_back(_store[i]);
+ }
+ setFlag(efHasDefaultValue);
+ }
+ }
+ return 0;
+}
+
+
+template <typename T>
+void OptionStorageTemplate<T>::clear()
+{
+ _values->clear();
+}
+
+
+template <typename T>
+int OptionStorageTemplate<T>::addValue(const T &value)
+{
+ int rc = incrementValueCount();
+ if (rc == 0)
+ {
+ _values->push_back(value);
+ }
+ return rc;
+}
+
+
+template <typename T>
+void OptionStorageTemplate<T>::processValues(int nvalues, bool bDoArray)
+{
+ if (_nvalptr)
+ {
+ *_nvalptr = _values->size();
+ }
+ if (bDoArray && _storeArray != NULL)
+ {
+ assert(*_storeArray == NULL);
+ *_storeArray = new T[_values->size()];
+ }
+ for (size_t i = _values->size() - nvalues; i < _values->size(); ++i)
+ {
+ if (_store)
+ {
+ _store[i] = (*_values)[i];
+ }
+ if (bDoArray && _storeArray != NULL)
+ {
+ (*_storeArray)[i] = (*_values)[i];
+ }
+ }
+}
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in optionsvisitor.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include "gromacs/options/optionsvisitor.h"
+
+#include "gromacs/options/abstractoptionstorage.h"
+#include "gromacs/options/options.h"
+
+#include "options-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * OptionInfo
+ */
+
+OptionInfo::OptionInfo(const AbstractOptionStorage &option)
+ : _option(option)
+{
+}
+
+bool OptionInfo::isBoolean() const
+{
+ return _option.isBoolean();
+}
+
+bool OptionInfo::isFile() const
+{
+ return _option.isFile();
+}
+
+bool OptionInfo::isHidden() const
+{
+ return _option.isHidden();
+}
+
+const std::string &OptionInfo::name() const
+{
+ return _option.name();
+}
+
+const std::string &OptionInfo::description() const
+{
+ return _option.description();
+}
+
+const char *OptionInfo::type() const
+{
+ return _option.typeString();
+}
+
+int OptionInfo::valueCount() const
+{
+ return _option.valueCount();
+}
+
+std::string OptionInfo::formatValue(int i) const
+{
+ return _option.formatValue(i);
+}
+
+std::string OptionInfo::formatValues() const
+{
+ std::string result;
+ int count = valueCount();
+ for (int i = 0; i < count; ++i)
+ {
+ if (i != 0)
+ {
+ result.append(" ");
+ }
+ result.append(formatValue(i));
+ }
+ return result;
+}
+
+/********************************************************************
+ * OptionsIterator
+ */
+
+OptionsIterator::OptionsIterator(const Options &options)
+ : _options(options)
+{
+}
+
+void OptionsIterator::acceptSubSections(OptionsVisitor *visitor) const
+{
+ const Options::Impl::SubSectionList &subSectionList =
+ _options._impl->_subSections;
+ Options::Impl::SubSectionList::const_iterator i;
+ for (i = subSectionList.begin(); i != subSectionList.end(); ++i)
+ {
+ visitor->visitSubSection(*(*i));
+ }
+}
+
+void OptionsIterator::acceptOptions(OptionsVisitor *visitor) const
+{
+ const Options::Impl::OptionList &optionList =
+ _options._impl->_options;
+ Options::Impl::OptionList::const_iterator i;
+ for (i = optionList.begin(); i != optionList.end(); ++i)
+ {
+ visitor->visitOption(OptionInfo(*(*i)));
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionsVisitor interface and supporting classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSVISITOR_H
+#define GMX_OPTIONS_OPTIONSVISITOR_H
+
+#include <string>
+
+namespace gmx
+{
+
+class AbstractOptionStorage;
+class Options;
+
+/*! \libinternal \brief
+ * Wrapper class for accessing option information.
+ *
+ * This class isolates the details of the internal option implementation
+ * from option visitors.
+ *
+ * \see OptionsVisitor
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionInfo
+{
+ public:
+ /*! \brief
+ * Wraps a given option object.
+ */
+ OptionInfo(const AbstractOptionStorage &option);
+
+ //! Returns true if the option is a boolean option.
+ bool isBoolean() const;
+ //! Returns true if the option is a file name option.
+ bool isFile() const;
+ //! Returns true if the option is a hidden option.
+ bool isHidden() const;
+ //! Returns the name of the option.
+ const std::string &name() const;
+ //! Returns the description of the option.
+ const std::string &description() const;
+ //! Returns the type of the option as a string.
+ const char *type() const;
+ //! Returns the number of values given for the option.
+ int valueCount() const;
+ //! Returns the i'th value of the option as a string.
+ std::string formatValue(int i) const;
+ //! Returns all the values of the option as a single string.
+ std::string formatValues() const;
+
+ private:
+ //! The wrapped option.
+ const AbstractOptionStorage &_option;
+
+ // Disallow copy and assign.
+ OptionInfo(const OptionInfo &);
+ void operator =(const OptionInfo &);
+};
+
+/*! \libinternal \brief
+ * Pure interface for visiting options in a Options object.
+ *
+ * \see OptionsIterator
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsVisitor
+{
+ public:
+ virtual ~OptionsVisitor() {}
+
+ /*! \brief
+ * Called for each subsection in Options.
+ */
+ virtual void visitSubSection(const Options §ion) = 0;
+ /*! \brief
+ * Called for each option in Options.
+ */
+ virtual void visitOption(const OptionInfo &option) = 0;
+};
+
+/*! \libinternal \brief
+ * Decorator class for visiting options in a Options object.
+ *
+ * This class provides an interface for looping through subsections and
+ * options in a Options object.
+ *
+ * Typical use (loop over all options, iteratively descending into
+ * subsections):
+ * \code
+class Visitor : public gmx::options::OptionsVisitor
+{
+ public:
+ void visitSubSection(const Options §ion)
+ {
+ OptionsIterator iterator(section);
+ iterator.acceptSubSections(this);
+ iterator.acceptOptions(this);
+ }
+
+ void visitOption(const OptionInfo &option)
+ {
+ // Do something.
+ }
+}
+
+Visitor().visitSubSection(options);
+ * \endcode
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsIterator
+{
+ public:
+ /*! \brief
+ * Creates an object for visiting options in a Options object.
+ */
+ OptionsIterator(const Options &options);
+
+ /*! \brief
+ * Visits each subsection in the wrapped Options object.
+ */
+ void acceptSubSections(OptionsVisitor *visitor) const;
+ /*! \brief
+ * Visits each option in the wrapped Options object.
+ */
+ void acceptOptions(OptionsVisitor *visitor) const;
+
+ private:
+ //! The wrapped Options object.
+ const Options &_options;
+
+ // Disallow copy and assign.
+ OptionsIterator(const OptionsIterator &);
+ void operator =(const OptionsIterator &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+options-test
--- /dev/null
+IF (GTEST_FOUND)
+ include_directories(${GTEST_INCLUDE_DIRS})
+ set(UNITTEST_SOURCES cmdlineparser.cpp option.cpp optionsassigner.cpp)
+ IF (GMOCK_FOUND)
+ include_directories(${GMOCK_INCLUDE_DIRS})
+ list(APPEND UNITTEST_SOURCES
+ abstractoptionstorage.cpp test_gmock_main.cpp)
+ ELSE (GMOCK_FOUND)
+ list(APPEND UNITTEST_SOURCES test_main.cpp)
+ ENDIF (GMOCK_FOUND)
+ add_executable(options-test ${UNITTEST_SOURCES})
+ target_link_libraries(options-test libgromacs ${GTEST_LIBRARIES})
+ IF (GMOCK_FOUND)
+ target_link_libraries(options-test ${GMOCK_LIBRARIES})
+ ENDIF (GMOCK_FOUND)
+ add_test(OptionsUnitTests options-test)
+ENDIF (GTEST_FOUND)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests proper handling of option storage.
+ *
+ * These tests check that methods in storage objects are called properly in all
+ * situations, and also that the OptionStorageTemplate class behaves properly.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include <vector>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/errorreporting/emptyerrorreporter.h"
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionstoragetemplate.h"
+#include "gromacs/options/optionsassigner.h"
+
+class MockOption;
+
+/*! \internal \brief
+ * Mock implementation of an option storage class for unit testing.
+ *
+ * Provides facilities for checking that correct methods are called, and for
+ * controlling how they add values using the base class methods.
+ *
+ * \ingroup module_options
+ */
+class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
+{
+ public:
+ /*! \brief
+ * Initializes the storage from option settings.
+ *
+ * \param[in] settings Storage settings.
+ * \param[in] options Options object.
+ * \retval 0 on success.
+ */
+ int init(const MockOption &settings, gmx::Options *options);
+
+ /*! \brief
+ * Calls addValue() in the base class and expects it to succeed.
+ */
+ void addValue(const std::string &value)
+ {
+ EXPECT_EQ(0, MyBase::addValue(value));
+ }
+ /*! \brief
+ * Calls addValue() in the base class and expects it to fail.
+ */
+ void addValueExpectFail(const std::string &value)
+ {
+ EXPECT_NE(0, MyBase::addValue(value));
+ }
+ /*! \brief
+ * Calls processAll() in the base class.
+ */
+ int processAllBase(gmx::AbstractErrorReporter *errors)
+ {
+ return MyBase::processAll(errors);
+ }
+ /*! \brief
+ * Calls addValue("dummy") in the base class and expects it to succeed.
+ */
+ void addDummyValue()
+ {
+ addValue("dummy");
+ }
+ /*! \brief
+ * Calls addValue("dummy") in the base class and expects it to fail.
+ */
+ void addDummyValueExpectFail()
+ {
+ addValueExpectFail("dummy");
+ }
+ /*! \brief
+ * Calls setFlag(efSet).
+ */
+ void setOption()
+ {
+ setFlag(gmx::efSet);
+ }
+
+ virtual const char *typeString() const { return "mock"; }
+ virtual std::string formatValue(int /*i*/) const { return ""; }
+
+ MOCK_METHOD2(convertValue, int(const std::string &value,
+ gmx::AbstractErrorReporter *errors));
+ MOCK_METHOD2(processSet, int(int nvalues,
+ gmx::AbstractErrorReporter *errors));
+ MOCK_METHOD1(processAll, int(gmx::AbstractErrorReporter *errors));
+};
+
+/*! \internal \brief
+ * Specifies an option that has a mock storage object for unit testing.
+ *
+ * \ingroup module_options
+ */
+class MockOption : public gmx::OptionTemplate<std::string, MockOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit MockOption(const char *name)
+ : MyBase(name), _storagePtr(NULL)
+ {
+ }
+
+ //! Sets the required flags to support storage that may not add values.
+ MyClass &mayNotAddValues()
+ { setFlag(gmx::efConversionMayNotAddValues); return me(); }
+ //! Sets an output pointer to give access to the created storage object.
+ MyClass &storageObject(MockOptionStorage **storagePtr)
+ { _storagePtr = storagePtr; return me(); }
+
+ private:
+ virtual int createDefaultStorage(gmx::Options *options,
+ gmx::AbstractOptionStorage **storage) const
+ {
+ int rc = gmx::createOptionStorage<MockOption, MockOptionStorage>(this, options, storage);
+ if (_storagePtr != NULL)
+ {
+ *_storagePtr = static_cast<MockOptionStorage *>(*storage);
+ }
+ return rc;
+ }
+
+ MockOptionStorage **_storagePtr;
+};
+
+int MockOptionStorage::init(const MockOption &settings, gmx::Options *options)
+{
+ using ::testing::_;
+ using ::testing::DoAll;
+ using ::testing::Invoke;
+ using ::testing::Return;
+ using ::testing::WithArg;
+ ON_CALL(*this, convertValue(_, _))
+ .WillByDefault(DoAll(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)),
+ Return(0)));
+ ON_CALL(*this, processAll(_))
+ .WillByDefault(Invoke(this, &MockOptionStorage::processAllBase));
+ return MyBase::init(settings, options);
+}
+
+namespace
+{
+
+/*
+ * Tests that finish() can set a required option even if the user has not
+ * provided it.
+ */
+TEST(AbstractOptionStorageTest, HandlesSetInFinish)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<std::string> values;
+ MockOptionStorage *mock;
+ options.addOption(MockOption("name").storageObject(&mock).required()
+ .storeVector(&values));
+
+ {
+ ::testing::InSequence dummy;
+ using ::testing::_;
+ using ::testing::DoAll;
+ using ::testing::Return;
+ using ::testing::InvokeWithoutArgs;
+ EXPECT_CALL(*mock, processAll(_))
+ .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::setOption),
+ InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
+ Return(0)));
+ }
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ ASSERT_EQ(1U, values.size());
+ EXPECT_EQ("dummy", values[0]);
+}
+
+/*
+ * Tests that storage works if the storage object does not add a value in a
+ * call to appendValue().
+ */
+TEST(AbstractOptionStorageTest, HandlesValueRemoval)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<std::string> values;
+ MockOptionStorage *mock;
+ options.addOption(MockOption("name").storageObject(&mock).mayNotAddValues()
+ .storeVector(&values).multiValue());
+
+ {
+ ::testing::InSequence dummy;
+ using ::testing::_;
+ using ::testing::Return;
+ EXPECT_CALL(*mock, convertValue("a", _));
+ EXPECT_CALL(*mock, convertValue("b", _))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock, convertValue("c", _));
+ EXPECT_CALL(*mock, processSet(2, _));
+ EXPECT_CALL(*mock, processAll(_));
+ }
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("name"));
+ EXPECT_EQ(0, assigner.appendValue("a"));
+ EXPECT_EQ(0, assigner.appendValue("b"));
+ EXPECT_EQ(0, assigner.appendValue("c"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ ASSERT_EQ(2U, values.size());
+ EXPECT_EQ("a", values[0]);
+ EXPECT_EQ("c", values[1]);
+}
+
+/*
+ * Tests that storage works if the storage object adds more than one value in
+ * one call to appendValue().
+ */
+TEST(AbstractOptionStorageTest, HandlesValueAddition)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<std::string> values;
+ MockOptionStorage *mock;
+ options.addOption(MockOption("name").storageObject(&mock)
+ .storeVector(&values).multiValue());
+
+ {
+ ::testing::InSequence dummy;
+ using ::testing::_;
+ using ::testing::DoAll;
+ using ::testing::InvokeWithoutArgs;
+ using ::testing::Return;
+ EXPECT_CALL(*mock, convertValue("a", _));
+ EXPECT_CALL(*mock, convertValue("b", _))
+ .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
+ InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
+ Return(0)));
+ EXPECT_CALL(*mock, processSet(3, _));
+ EXPECT_CALL(*mock, processAll(_));
+ }
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("name"));
+ EXPECT_EQ(0, assigner.appendValue("a"));
+ EXPECT_EQ(0, assigner.appendValue("b"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ ASSERT_EQ(3U, values.size());
+ EXPECT_EQ("a", values[0]);
+ EXPECT_EQ("dummy", values[1]);
+ EXPECT_EQ("dummy", values[2]);
+}
+
+/*
+ * Tests that storage works if the storage object adds more than one value in
+ * one call to appendValue(), and this results in too many values.
+ */
+TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<std::string> values;
+ MockOptionStorage *mock;
+ options.addOption(MockOption("name").storageObject(&mock)
+ .storeVector(&values).valueCount(2));
+
+ {
+ ::testing::InSequence dummy;
+ using ::testing::_;
+ using ::testing::DoAll;
+ using ::testing::InvokeWithoutArgs;
+ using ::testing::Return;
+ EXPECT_CALL(*mock, convertValue("a", _));
+ EXPECT_CALL(*mock, convertValue("b", _))
+ .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
+ InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValueExpectFail),
+ Return(gmx::eeInvalidInput)));
+ EXPECT_CALL(*mock, processSet(2, _));
+ EXPECT_CALL(*mock, processAll(_));
+ }
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("name"));
+ EXPECT_EQ(0, assigner.appendValue("a"));
+ EXPECT_NE(0, assigner.appendValue("b"));
+ EXPECT_NE(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ ASSERT_EQ(2U, values.size());
+ EXPECT_EQ("a", values[0]);
+ EXPECT_EQ("dummy", values[1]);
+}
+
+/*
+ * Tests that the storage object is properly invoked even if no output values
+ * should be produced.
+ */
+TEST(AbstractOptionStorageTest, AllowsEmptyValues)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<std::string> values;
+ MockOptionStorage *mock;
+ options.addOption(MockOption("name").storageObject(&mock).mayNotAddValues()
+ .storeVector(&values).valueCount(0));
+
+ {
+ ::testing::InSequence dummy;
+ using ::testing::_;
+ using ::testing::DoAll;
+ using ::testing::InvokeWithoutArgs;
+ using ::testing::Return;
+ EXPECT_CALL(*mock, convertValue("a", _))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock, processSet(0, _));
+ EXPECT_CALL(*mock, processAll(_));
+ }
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("name"));
+ EXPECT_EQ(0, assigner.appendValue("a"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ ASSERT_EQ(0U, values.size());
+}
+
+} // namespace
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests gmx::CommandLineParser.
+ *
+ * These tests exercise a large fraction of the code, so they may
+ * catch errors in other parts than just in command-line parsing.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/errorreporting/emptyerrorreporter.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/cmdlineparser.h"
+#include "gromacs/options/options.h"
+
+namespace
+{
+
+class CommandLineParserTest : public ::testing::Test
+{
+ public:
+ CommandLineParserTest();
+ ~CommandLineParserTest();
+
+ void createArguments(const char *cmdline[]);
+
+ gmx::Options _options;
+ gmx::EmptyErrorReporter _errors;
+ gmx::CommandLineParser _parser;
+ bool _flag;
+ std::vector<int> _ivalues;
+ std::vector<double> _dvalues;
+ int _argc;
+ char **_argv;
+};
+
+CommandLineParserTest::CommandLineParserTest()
+ : _options(NULL, NULL), _parser(&_options, &_errors),
+ _flag(false),
+ _argc(0), _argv(NULL)
+{
+ using gmx::BooleanOption;
+ using gmx::IntegerOption;
+ using gmx::DoubleOption;
+ _options.addOption(BooleanOption("flag").store(&_flag));
+ _options.addOption(IntegerOption("mvi").storeVector(&_ivalues).multiValue());
+ _options.addOption(DoubleOption("mvd").storeVector(&_dvalues).allowMultiple());
+}
+
+CommandLineParserTest::~CommandLineParserTest()
+{
+ if (_argv != NULL)
+ {
+ for (int i = 0; i < _argc; ++i)
+ {
+ free(_argv[i]);
+ }
+ }
+ delete [] _argv;
+}
+
+void CommandLineParserTest::createArguments(const char *cmdline[])
+{
+ _argc = 0;
+ while (cmdline[_argc] != NULL) ++_argc;
+ ++_argc;
+
+ _argv = new char *[_argc];
+ _argv[0] = strdup("test");
+ for (int i = 1; i < _argc; ++i)
+ {
+ _argv[i] = strdup(cmdline[i - 1]);
+ }
+}
+
+TEST_F(CommandLineParserTest, HandlesSingleValues)
+{
+ const char *cmdline[] = {"-flag", "yes", "-mvi", "2", "-mvd", "2.7", NULL};
+ createArguments(cmdline);
+ ASSERT_EQ(0, _parser.parse(&_argc, _argv));
+ ASSERT_EQ(0, _options.finish(&_errors));
+
+ EXPECT_TRUE(_flag);
+ ASSERT_EQ(1U, _ivalues.size());
+ EXPECT_EQ(2, _ivalues[0]);
+ ASSERT_EQ(1U, _dvalues.size());
+ EXPECT_DOUBLE_EQ(2.7, _dvalues[0]);
+}
+
+} // namespace
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests construction of basic option types.
+ *
+ * Most of the tests for the basic options are in optionsassigner.cpp.
+ * This file only tests behavior that should fail in parameter construction,
+ * which would result in higher-level code asserting.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include <vector>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/options/basicoptions.h"
+
+#include "../basicoptionstorage.h"
+
+namespace
+{
+
+TEST(OptionTest, SetsNameAndDescription)
+{
+ gmx::IntegerOptionStorage option;
+ int value = -1;
+ using gmx::IntegerOption;
+ ASSERT_EQ(0, option.init(IntegerOption("name").store(&value)
+ .description("Description"), NULL));
+ EXPECT_EQ("name", option.name());
+ EXPECT_EQ("Description", option.description());
+ EXPECT_FALSE(option.isSet());
+}
+
+TEST(OptionTest, FailsOnNonsafeStorage)
+{
+ gmx::IntegerOptionStorage option;
+ int value = -1;
+ using gmx::IntegerOption;
+ ASSERT_NE(0, option.init(IntegerOption("name").store(&value)
+ .multiValue(), NULL));
+}
+
+TEST(OptionTest, FailsOnIncorrectEnumDefaultValue)
+{
+ gmx::StringOptionStorage option;
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ using gmx::StringOption;
+ ASSERT_NE(0, option.init(StringOption("name").store(&value)
+ .enumValue(allowed)
+ .defaultValue("unknown"), NULL));
+}
+
+} // namespace
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests option assignment.
+ *
+ * In addition to testing gmx::OptionsAssigner, these are the main
+ * tests for the classes from basicoptions.h and basicoptionstorage.h (and
+ * their base classes) that actually implement the behavior, as well as for the
+ * internal implementation of the gmx::Options and gmx::Option classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_options
+ */
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/errorreporting/emptyerrorreporter.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionsassigner.h"
+
+namespace
+{
+
+TEST(OptionsAssignerTest, HandlesMissingRequiredParameter)
+{
+ gmx::Options options(NULL, NULL);
+ int value = 0;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value).required());
+
+ gmx::EmptyErrorReporter errors;
+ EXPECT_NE(0, options.finish(&errors));
+}
+
+TEST(OptionsAssignerTest, HandlesInvalidMultipleParameter)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<int> values;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").storeVector(&values).multiValue());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("1"));
+ EXPECT_NE(0, assigner.startOption("p"));
+ EXPECT_NE(0, assigner.finish());
+}
+
+TEST(OptionsAssignerTest, HandlesMultipleParameter)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<int> values;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").storeVector(&values).allowMultiple());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("1"));
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("2"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+ EXPECT_TRUE(options.isSet("p"));
+ ASSERT_EQ(2U, values.size());
+ EXPECT_EQ(1, values[0]);
+ EXPECT_EQ(2, values[1]);
+}
+
+TEST(OptionsAssignerTest, HandlesMissingValue)
+{
+ gmx::Options options(NULL, NULL);
+ int value1 = 0, value2 = 0;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value1));
+ options.addOption(IntegerOption("q").store(&value2));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.startOption("q"));
+ EXPECT_EQ(0, assigner.appendValue("2"));
+ EXPECT_NE(0, assigner.finish());
+}
+
+TEST(OptionsAssignerTest, HandlesExtraValue)
+{
+ gmx::Options options(NULL, NULL);
+ int value1 = 0;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value1));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("2"));
+ EXPECT_NE(0, assigner.appendValue("3"));
+ EXPECT_NE(0, assigner.finish());
+}
+
+TEST(OptionsAssignerTest, HandlesSubSections)
+{
+ gmx::Options options(NULL, NULL);
+ gmx::Options sub1("section1", NULL);
+ gmx::Options sub2("section2", NULL);
+ int value = 3;
+ int value1 = 1;
+ int value2 = 2;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value));
+ sub1.addOption(IntegerOption("p").store(&value1));
+ sub2.addOption(IntegerOption("p").store(&value2));
+ options.addSubSection(&sub1);
+ options.addSubSection(&sub2);
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startSubSection("section1"));
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("5"));
+ EXPECT_EQ(0, assigner.finishSubSection());
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("4"));
+ EXPECT_EQ(0, assigner.startSubSection("section2"));
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("6"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(4, value);
+ EXPECT_EQ(5, value1);
+ EXPECT_EQ(6, value2);
+}
+
+TEST(OptionsAssignerTest, HandlesNoStrictSubSections)
+{
+ gmx::Options options(NULL, NULL);
+ gmx::Options sub1("section1", NULL);
+ gmx::Options sub2("section2", NULL);
+ int pvalue = 3;
+ int pvalue1 = 1;
+ int qvalue = 4;
+ int pvalue2 = 2;
+ int rvalue = 5;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&pvalue));
+ sub1.addOption(IntegerOption("p").store(&pvalue1));
+ sub1.addOption(IntegerOption("q").store(&qvalue));
+ sub2.addOption(IntegerOption("p").store(&pvalue2));
+ sub2.addOption(IntegerOption("r").store(&rvalue));
+ options.addSubSection(&sub1);
+ options.addSubSection(&sub2);
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ assigner.setNoStrictSectioning(true);
+ EXPECT_EQ(0, assigner.startOption("q"));
+ EXPECT_EQ(0, assigner.appendValue("6"));
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("7"));
+ EXPECT_EQ(0, assigner.startOption("r"));
+ EXPECT_EQ(0, assigner.appendValue("8"));
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("9"));
+ EXPECT_EQ(0, assigner.finishSubSection());
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("10"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(6, qvalue);
+ EXPECT_EQ(7, pvalue1);
+ EXPECT_EQ(8, rvalue);
+ EXPECT_EQ(9, pvalue2);
+ EXPECT_EQ(10, pvalue);
+}
+
+TEST(OptionsAssignerTest, HandlesMultipleSources)
+{
+ gmx::Options options(NULL, NULL);
+ int value = -1;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.start());
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("1"));
+ EXPECT_EQ(0, assigner.finish());
+ gmx::OptionsAssigner assigner2(&options, &errors);
+ EXPECT_EQ(0, assigner2.start());
+ EXPECT_EQ(0, assigner2.startOption("p"));
+ EXPECT_EQ(0, assigner2.appendValue("2"));
+ EXPECT_EQ(0, assigner2.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(2, value);
+}
+
+
+TEST(OptionsAssignerBooleanTest, StoresYesValue)
+{
+ gmx::Options options(NULL, NULL);
+ bool value = false;
+ using gmx::BooleanOption;
+ options.addOption(BooleanOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.appendValue("yes"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_TRUE(value);
+}
+
+TEST(OptionsAssignerBooleanTest, SetsBooleanWithoutExplicitValue)
+{
+ gmx::Options options(NULL, NULL);
+ bool value = false;
+ using gmx::BooleanOption;
+ options.addOption(BooleanOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.startOption("p"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_TRUE(value);
+}
+
+TEST(OptionsAssignerBooleanTest, ClearsBooleanWithPrefixNo)
+{
+ gmx::Options options(NULL, NULL);
+ bool value = true;
+ using gmx::BooleanOption;
+ options.addOption(BooleanOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ assigner.setAcceptBooleanNoPrefix(true);
+ EXPECT_EQ(0, assigner.startOption("nop"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_FALSE(value);
+}
+
+TEST(OptionsAssignerBooleanTest, HandlesBooleanWithPrefixAndValue)
+{
+ gmx::Options options(NULL, NULL);
+ bool value = false;
+ using gmx::BooleanOption;
+ options.addOption(BooleanOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ assigner.setAcceptBooleanNoPrefix(true);
+ EXPECT_EQ(0, assigner.startOption("nop"));
+ assigner.appendValue("no");
+ int rc = assigner.finish();
+ // It's OK to fail, but if it doesn't, it should work.
+ if (rc == 0)
+ {
+ EXPECT_EQ(0, options.finish(&errors));
+ EXPECT_TRUE(value);
+ }
+}
+
+
+TEST(OptionsAssignerIntegerTest, StoresSingleValue)
+{
+ gmx::Options options(NULL, NULL);
+ int value = 1;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("3"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(3, value);
+}
+
+TEST(OptionsAssignerIntegerTest, StoresDefaultValue)
+{
+ gmx::Options options(NULL, NULL);
+ int value = -1;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(&value).defaultValue(2));
+ EXPECT_EQ(2, value);
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(2, value);
+}
+
+TEST(OptionsAssignerIntegerTest, StoresToArray)
+{
+ gmx::Options options(NULL, NULL);
+ int *values = NULL;
+ int nval = -1;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").storeArray(&values, &nval).multiValue());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("-2"));
+ ASSERT_EQ(0, assigner.appendValue("1"));
+ ASSERT_EQ(0, assigner.appendValue("4"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(3, nval);
+ EXPECT_EQ(-2, values[0]);
+ EXPECT_EQ(1, values[1]);
+ EXPECT_EQ(4, values[2]);
+ delete[] values;
+}
+
+TEST(OptionsAssignerIntegerTest, StoresToVector)
+{
+ gmx::Options options(NULL, NULL);
+ std::vector<int> values;
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").storeVector(&values).multiValue());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("-2"));
+ ASSERT_EQ(0, assigner.appendValue("1"));
+ ASSERT_EQ(0, assigner.appendValue("4"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(3U, values.size());
+ EXPECT_EQ(-2, values[0]);
+ EXPECT_EQ(1, values[1]);
+ EXPECT_EQ(4, values[2]);
+}
+
+TEST(OptionsAssignerIntegerTest, HandlesVectors)
+{
+ gmx::Options options(NULL, NULL);
+ int vec[3] = {0, 0, 0};
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(vec).vector());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("-2"));
+ ASSERT_EQ(0, assigner.appendValue("1"));
+ ASSERT_EQ(0, assigner.appendValue("4"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(-2, vec[0]);
+ EXPECT_EQ(1, vec[1]);
+ EXPECT_EQ(4, vec[2]);
+}
+
+TEST(OptionsAssignerIntegerTest, HandlesVectorFromSingleValue)
+{
+ gmx::Options options(NULL, NULL);
+ int vec[3] = {0, 0, 0};
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(vec).vector());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("2"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(2, vec[0]);
+ EXPECT_EQ(2, vec[1]);
+ EXPECT_EQ(2, vec[2]);
+}
+
+TEST(OptionsAssignerIntegerTest, HandlesVectorsWithDefaultValue)
+{
+ gmx::Options options(NULL, NULL);
+ int vec[3] = {3, 2, 1};
+ using gmx::IntegerOption;
+ options.addOption(IntegerOption("p").store(vec).vector());
+
+ gmx::EmptyErrorReporter errors;
+ EXPECT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ(3, vec[0]);
+ EXPECT_EQ(2, vec[1]);
+ EXPECT_EQ(1, vec[2]);
+}
+
+
+TEST(OptionsAssignerDoubleTest, StoresSingleValue)
+{
+ gmx::Options options(NULL, NULL);
+ double value = 0.0;
+ using gmx::DoubleOption;
+ options.addOption(DoubleOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("2.7"));
+ ASSERT_EQ(0, assigner.finish());
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_DOUBLE_EQ(2.7, value);
+}
+
+
+TEST(OptionsAssignerStringTest, StoresSingleValue)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("value"));
+ ASSERT_EQ(0, assigner.finish());
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ("value", value);
+}
+
+TEST(OptionsAssignerStringTest, HandlesEnumValue)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ int index = -1;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value)
+ .enumValue(allowed)
+ .storeEnumIndex(&index));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("test"));
+ ASSERT_EQ(0, assigner.finish());
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ("test", value);
+ EXPECT_EQ(1, index);
+}
+
+TEST(OptionsAssignerStringTest, HandlesIncorrectEnumValue)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ int index = -1;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value)
+ .enumValue(allowed)
+ .storeEnumIndex(&index));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_NE(0, assigner.appendValue("unknown"));
+}
+
+TEST(OptionsAssignerStringTest, CompletesEnumValue)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ int index = -1;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value)
+ .enumValue(allowed)
+ .storeEnumIndex(&index));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.startOption("p"));
+ ASSERT_EQ(0, assigner.appendValue("te"));
+ ASSERT_EQ(0, assigner.finish());
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ("test", value);
+ EXPECT_EQ(1, index);
+}
+
+TEST(OptionsAssignerStringTest, HandlesEnumWithNoValue)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ int index = -3;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value)
+ .enumValue(allowed)
+ .storeEnumIndex(&index));
+ EXPECT_TRUE(value.empty());
+ EXPECT_EQ(-1, index);
+
+ gmx::EmptyErrorReporter errors;
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_TRUE(value.empty());
+ EXPECT_EQ(-1, index);
+}
+
+TEST(OptionsAssignerStringTest, HandlesEnumDefaultValue)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ int index = -1;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value)
+ .enumValue(allowed)
+ .defaultValue("test")
+ .storeEnumIndex(&index));
+ EXPECT_EQ("test", value);
+ EXPECT_EQ(1, index);
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.finish());
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ("test", value);
+ EXPECT_EQ(1, index);
+}
+
+TEST(OptionsAssignerStringTest, HandlesEnumDefaultIndex)
+{
+ gmx::Options options(NULL, NULL);
+ std::string value;
+ const char * const allowed[] = { "none", "test", "value", NULL };
+ int index = -1;
+ using gmx::StringOption;
+ options.addOption(StringOption("p").store(&value)
+ .enumValue(allowed)
+ .defaultEnumIndex(1)
+ .storeEnumIndex(&index));
+ EXPECT_EQ("test", value);
+ EXPECT_EQ(1, index);
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&options, &errors);
+ ASSERT_EQ(0, assigner.finish());
+ ASSERT_EQ(0, options.finish(&errors));
+
+ EXPECT_EQ("test", value);
+ EXPECT_EQ(1, index);
+}
+
+} // namespace
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * main() for unit tests that use Google C++ Mocking Framework.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include <gmock/gmock.h>
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+/*! \brief
+ * Initializes unit testing with Google C++ Mocking Framework.
+ */
+int main(int argc, char *argv[])
+{
+ ::testing::InitGoogleMock(&argc, argv);
+ ::gmx::setFatalErrorHandler(NULL);
+ return RUN_ALL_TESTS();
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * main() for unit tests that use Google C++ Testing Framework.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+/*! \brief
+ * Initializes unit testing with Google C++ Testing Framework.
+ */
+int main(int argc, char *argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ ::gmx::setFatalErrorHandler(NULL);
+ return RUN_ALL_TESTS();
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \defgroup module_selection Parsing and Evaluation of Analysis Selections
+ * \ingroup group_analysismodules
+ * \brief
+ * Provides functionality for initializing and evaluating selections.
+ *
+ * \internal
+ * Implementation details of different parts of the module are discussed on
+ * separate pages:
+ * - \ref page_module_selection_custom
+ * - \ref page_module_selection_parser
+ * - \ref page_module_selection_compiler
+ * - \ref page_module_selection_insolidangle
+ */
+/*! \ingroup group_analysismodules
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+/*! \file
+ * \brief
+ * Public API convenience header for selection handling.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_H
+#define GMX_SELECTION_H
+
+#include "selection/selection.h"
+#include "selection/selectionoption.h"
+
+#endif
--- /dev/null
+file(GLOB SELECTION_SOURCES *.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${SELECTION_SOURCES} PARENT_SCOPE)
+
+set(SELECTION_PUBLIC_HEADERS
+ centerofmass.h
+ nbsearch.h
+ poscalc.h
+ indexutil.h
+ position.h
+ selection.h
+ selectionenums.h
+ selectionoption.h
+ selparam.h
+ selmethod.h
+ selvalue.h)
+install(FILES ${SELECTION_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/selection
+ COMPONENT development)
+
+if (BUILD_TESTING)
+ add_subdirectory(tests)
+endif (BUILD_TESTING)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in centerofmass.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <typedefs.h>
+#include <pbc.h>
+#include <vec.h>
+
+#include "gromacs/selection/centerofmass.h"
+
+/*!
+ * \param[in] top Topology structure (unused, can be NULL).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[out] xout COG position for the indexed atoms.
+ * \returns 0 on success.
+ */
+int
+gmx_calc_cog(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout)
+{
+ int m, j, ai;
+
+ clear_rvec(xout);
+ for (m = 0; m < nrefat; ++m)
+ {
+ ai = index[m];
+ rvec_inc(xout, x[ai]);
+ }
+ svmul(1.0/nrefat, xout, xout);
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses.
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[out] xout COM position for the indexed atoms.
+ * \returns 0 on success, EINVAL if \p top is NULL.
+ *
+ * Works exactly as gmx_calc_cog() with the exception that a center of
+ * mass are calculated, and hence a topology with masses is required.
+ */
+int
+gmx_calc_com(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout)
+{
+ int m, j, ai;
+ real mass, mtot;
+
+ if (!top)
+ {
+ gmx_incons("no masses available while mass weighting was requested");
+ return EINVAL;
+ }
+ clear_rvec(xout);
+ mtot = 0;
+ for (m = 0; m < nrefat; ++m)
+ {
+ ai = index[m];
+ mass = top->atoms.atom[ai].m;
+ for (j = 0; j < DIM; ++j)
+ {
+ xout[j] += mass * x[ai][j];
+ }
+ mtot += mass;
+ }
+ svmul(1.0/mtot, xout, xout);
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses.
+ * \param[in] f Forces on all atoms.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[out] fout Force on the COG position for the indexed atoms.
+ * \returns 0 on success, EINVAL if \p top is NULL.
+ *
+ * No special function is provided for calculating the force on the center of
+ * mass, because this can be achieved with gmx_calc_cog().
+ */
+int
+gmx_calc_cog_f(t_topology *top, rvec f[], int nrefat, atom_id index[], rvec fout)
+{
+ int m, j, ai;
+ real mass, mtot;
+
+ if (!top)
+ {
+ gmx_incons("no masses available while mass weighting was needed");
+ return EINVAL;
+ }
+ clear_rvec(fout);
+ mtot = 0;
+ for (m = 0; m < nrefat; ++m)
+ {
+ ai = index[m];
+ mass = top->atoms.atom[ai].m;
+ for (j = 0; j < DIM; ++j)
+ {
+ fout[j] += f[ai][j] / mass;
+ }
+ mtot += mass;
+ }
+ svmul(mtot, fout, fout);
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==FALSE).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[in] bMass If TRUE, mass weighting is used.
+ * \param[out] xout COM/COG position for the indexed atoms.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
+ *
+ * Calls either gmx_calc_com() or gmx_calc_cog() depending on the value of
+ * \p bMass.
+ * Other parameters are passed unmodified to these functions.
+ */
+int
+gmx_calc_comg(t_topology *top, rvec x[], int nrefat, atom_id index[],
+ gmx_bool bMass, rvec xout)
+{
+ if (bMass)
+ {
+ return gmx_calc_com(top, x, nrefat, index, xout);
+ }
+ else
+ {
+ return gmx_calc_cog(top, x, nrefat, index, xout);
+ }
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==TRUE).
+ * \param[in] f Forces on all atoms.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[in] bMass If TRUE, force on COM is calculated.
+ * \param[out] fout Force on the COM/COG position for the indexed atoms.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is FALSE.
+ *
+ * Calls either gmx_calc_cog() or gmx_calc_cog_f() depending on the value of
+ * \p bMass.
+ * Other parameters are passed unmodified to these functions.
+ */
+int
+gmx_calc_comg_f(t_topology *top, rvec f[], int nrefat, atom_id index[],
+ gmx_bool bMass, rvec fout)
+{
+ if (bMass)
+ {
+ return gmx_calc_cog(top, f, nrefat, index, fout);
+ }
+ else
+ {
+ return gmx_calc_cog_f(top, f, nrefat, index, fout);
+ }
+}
+
+
+/*!
+ * \param[in] top Topology structure (unused, can be NULL).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] pbc Periodic boundary conditions structure.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[out] xout COG position for the indexed atoms.
+ * \returns 0 on success.
+ *
+ * Works exactly as gmx_calc_com_pbc(), but calculates the center of geometry.
+ */
+int
+gmx_calc_cog_pbc(t_topology *top, rvec x[], t_pbc *pbc,
+ int nrefat, atom_id index[], rvec xout)
+{
+ const real tol = 1e-4;
+ gmx_bool bChanged;
+ int m, j, ai, iter;
+ rvec dx, xtest;
+
+ /* First simple calculation */
+ gmx_calc_cog(top, x, nrefat, index, xout);
+ /* Now check if any atom is more than half the box from the COM */
+ if (pbc)
+ {
+ iter = 0;
+ do
+ {
+ bChanged = FALSE;
+ for (m = 0; m < nrefat; ++m)
+ {
+ ai = index[m];
+ pbc_dx(pbc, x[ai], xout, dx);
+ rvec_add(xout, dx, xtest);
+ for (j = 0; j < DIM; ++j)
+ {
+ if (fabs(xtest[j] - x[ai][j]) > tol)
+ {
+ /* Here we have used the wrong image for contributing to the COM */
+ xout[j] += (xtest[j] - x[ai][j]) / nrefat;
+ x[ai][j] = xtest[j];
+ bChanged = TRUE;
+ }
+ }
+ }
+ iter++;
+ }
+ while (bChanged);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses.
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] pbc Periodic boundary conditions structure.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[out] xout COM position for the indexed atoms.
+ * \returns 0 on success, EINVAL if \p top is NULL.
+ *
+ * Works as gmx_calc_com(), but takes into account periodic boundary
+ * conditions: If any atom is more than half the box from the COM,
+ * it is wrapped around and a new COM is calculated. This is repeated
+ * until no atoms violate the condition.
+ *
+ * Modified from src/tools/gmx_sorient.c in Gromacs distribution.
+ */
+int
+gmx_calc_com_pbc(t_topology *top, rvec x[], t_pbc *pbc,
+ int nrefat, atom_id index[], rvec xout)
+{
+ const real tol = 1e-4;
+ gmx_bool bChanged;
+ int m, j, ai, iter;
+ real mass, mtot;
+ rvec dx, xtest;
+
+ if (!top)
+ {
+ gmx_incons("no masses available while mass weighting was requested");
+ return EINVAL;
+ }
+ /* First simple calculation */
+ clear_rvec(xout);
+ mtot = 0;
+ for (m = 0; m < nrefat; ++m)
+ {
+ ai = index[m];
+ mass = top->atoms.atom[ai].m;
+ for (j = 0; j < DIM; ++j)
+ {
+ xout[j] += mass * x[ai][j];
+ }
+ mtot += mass;
+ }
+ svmul(1.0/mtot, xout, xout);
+ /* Now check if any atom is more than half the box from the COM */
+ if (pbc)
+ {
+ iter = 0;
+ do
+ {
+ bChanged = FALSE;
+ for (m = 0; m < nrefat; ++m)
+ {
+ ai = index[m];
+ mass = top->atoms.atom[ai].m / mtot;
+ pbc_dx(pbc, x[ai], xout, dx);
+ rvec_add(xout, dx, xtest);
+ for (j = 0; j < DIM; ++j)
+ {
+ if (fabs(xtest[j] - x[ai][j]) > tol)
+ {
+ /* Here we have used the wrong image for contributing to the COM */
+ xout[j] += mass * (xtest[j] - x[ai][j]);
+ x[ai][j] = xtest[j];
+ bChanged = TRUE;
+ }
+ }
+ }
+ iter++;
+ }
+ while (bChanged);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==FALSE).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] pbc Periodic boundary conditions structure.
+ * \param[in] nrefat Number of atoms in the index.
+ * \param[in] index Indices of atoms.
+ * \param[in] bMass If TRUE, mass weighting is used.
+ * \param[out] xout COM/COG position for the indexed atoms.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
+ *
+ * Calls either gmx_calc_com() or gmx_calc_cog() depending on the value of
+ * \p bMass.
+ * Other parameters are passed unmodified to these functions.
+ */
+int
+gmx_calc_comg_pbc(t_topology *top, rvec x[], t_pbc *pbc,
+ int nrefat, atom_id index[], gmx_bool bMass, rvec xout)
+{
+ if (bMass)
+ {
+ return gmx_calc_com_pbc(top, x, pbc, nrefat, index, xout);
+ }
+ else
+ {
+ return gmx_calc_cog_pbc(top, x, pbc, nrefat, index, xout);
+ }
+}
+
+
+/*!
+ * \param[in] top Topology structure (unused, can be NULL).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] block t_block structure that divides \p index into blocks.
+ * \param[in] index Indices of atoms.
+ * \param[out] xout \p block->nr COG positions.
+ * \returns 0 on success.
+ */
+int
+gmx_calc_cog_block(t_topology *top, rvec x[], t_block *block, atom_id index[],
+ rvec xout[])
+{
+ int b, i, ai;
+ rvec xb;
+
+ for (b = 0; b < block->nr; ++b)
+ {
+ clear_rvec(xb);
+ for (i = block->index[b]; i < block->index[b+1]; ++i)
+ {
+ ai = index[i];
+ rvec_inc(xb, x[ai]);
+ }
+ svmul(1.0/(block->index[b+1] - block->index[b]), xb, xout[b]);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses.
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] block t_block structure that divides \p index into blocks.
+ * \param[in] index Indices of atoms.
+ * \param[out] xout \p block->nr COM positions.
+ * \returns 0 on success, EINVAL if \p top is NULL.
+ *
+ * Works exactly as gmx_calc_cog_block() with the exception that centers of
+ * mass are calculated, and hence a topology with masses is required.
+ */
+int
+gmx_calc_com_block(t_topology *top, rvec x[], t_block *block, atom_id index[],
+ rvec xout[])
+{
+ int b, i, ai, d;
+ rvec xb;
+ real mass, mtot;
+
+ if (!top)
+ {
+ gmx_incons("no masses available while mass weighting was requested");
+ return EINVAL;
+ }
+ for (b = 0; b < block->nr; ++b)
+ {
+ clear_rvec(xb);
+ mtot = 0;
+ for (i = block->index[b]; i < block->index[b+1]; ++i)
+ {
+ ai = index[i];
+ mass = top->atoms.atom[ai].m;
+ for (d = 0; d < DIM; ++d)
+ {
+ xb[d] += mass * x[ai][d];
+ }
+ mtot += mass;
+ }
+ svmul(1.0/mtot, xb, xout[b]);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses.
+ * \param[in] f Forces on all atoms.
+ * \param[in] block t_block structure that divides \p index into blocks.
+ * \param[in] index Indices of atoms.
+ * \param[out] fout \p block->nr Forces on COG positions.
+ * \returns 0 on success, EINVAL if \p top is NULL.
+ */
+int
+gmx_calc_cog_f_block(t_topology *top, rvec f[], t_block *block, atom_id index[],
+ rvec fout[])
+{
+ int b, i, ai, d;
+ rvec fb;
+ real mass, mtot;
+
+ if (!top)
+ {
+ gmx_incons("no masses available while mass weighting was needed");
+ return EINVAL;
+ }
+ for (b = 0; b < block->nr; ++b)
+ {
+ clear_rvec(fb);
+ mtot = 0;
+ for (i = block->index[b]; i < block->index[b+1]; ++i)
+ {
+ ai = index[i];
+ mass = top->atoms.atom[ai].m;
+ for (d = 0; d < DIM; ++d)
+ {
+ fb[d] += f[ai][d] / mass;
+ }
+ mtot += mass;
+ }
+ svmul(mtot, fb, fout[b]);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==FALSE).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] block t_block structure that divides \p index into blocks.
+ * \param[in] index Indices of atoms.
+ * \param[in] bMass If TRUE, mass weighting is used.
+ * \param[out] xout \p block->nr COM/COG positions.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
+ *
+ * Calls either gmx_calc_com_block() or gmx_calc_cog_block() depending on the
+ * value of \p bMass.
+ * Other parameters are passed unmodified to these functions.
+ */
+int
+gmx_calc_comg_block(t_topology *top, rvec x[], t_block *block, atom_id index[],
+ gmx_bool bMass, rvec xout[])
+{
+ if (bMass)
+ {
+ return gmx_calc_com_block(top, x, block, index, xout);
+ }
+ else
+ {
+ return gmx_calc_cog_block(top, x, block, index, xout);
+ }
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==FALSE).
+ * \param[in] f Forces on all atoms.
+ * \param[in] block t_block structure that divides \p index into blocks.
+ * \param[in] index Indices of atoms.
+ * \param[in] bMass If TRUE, force on COM is calculated.
+ * \param[out] fout \p block->nr forces on the COM/COG positions.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
+ *
+ * Calls either gmx_calc_com_block() or gmx_calc_cog_block() depending on the
+ * value of \p bMass.
+ * Other parameters are passed unmodified to these functions.
+ */
+int
+gmx_calc_comg_f_block(t_topology *top, rvec f[], t_block *block, atom_id index[],
+ gmx_bool bMass, rvec fout[])
+{
+ if (bMass)
+ {
+ return gmx_calc_cog_block(top, f, block, index, fout);
+ }
+ else
+ {
+ return gmx_calc_cog_f_block(top, f, block, index, fout);
+ }
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==FALSE).
+ * \param[in] x Position vectors of all atoms.
+ * \param[in] block Blocks for calculation.
+ * \param[in] bMass If TRUE, mass weighting is used.
+ * \param[out] xout \p block->nr COM/COG positions.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is TRUE.
+ *
+ * Calls gmx_calc_comg_block(), converting the \p t_blocka structure into
+ * a \p t_block and an index. Other parameters are passed unmodified.
+ *
+ * \attention
+ * This function assumes that a pointer to \c t_blocka can be safely typecast
+ * into \c t_block such that the index fields can still be referenced.
+ * With the present Gromacs defitions of these types, this is the case,
+ * but if the layout of these structures is changed, this may lead to strange
+ * crashes.
+ */
+int
+gmx_calc_comg_blocka(t_topology *top, rvec x[], t_blocka *block,
+ gmx_bool bMass, rvec xout[])
+{
+ /* TODO: It would probably be better to do this without the type cast */
+ return gmx_calc_comg_block(top, x, (t_block *)block, block->a, bMass, xout);
+}
+
+/*!
+ * \param[in] top Topology structure with masses
+ * (can be NULL if \p bMASS==TRUE).
+ * \param[in] f Forces on all atoms.
+ * \param[in] block Blocks for calculation.
+ * \param[in] bMass If TRUE, force on COM is calculated.
+ * \param[out] fout \p block->nr forces on the COM/COG positions.
+ * \returns 0 on success, EINVAL if \p top is NULL and \p bMass is FALSE.
+ *
+ * Calls gmx_calc_comg_f_block(), converting the \p t_blocka structure into
+ * a \p t_block and an index. Other parameters are passed unmodified.
+ *
+ * \attention
+ * This function assumes that a pointer to \c t_blocka can be safely typecast
+ * into \c t_block such that the index fields can still be referenced.
+ * With the present Gromacs defitions of these types, this is the case,
+ * but if the layout of these structures is changed, this may lead to strange
+ * crashes.
+ */
+int
+gmx_calc_comg_f_blocka(t_topology *top, rvec f[], t_blocka *block,
+ gmx_bool bMass, rvec fout[])
+{
+ /* TODO: It would probably be better to do this without the type cast */
+ return gmx_calc_comg_f_block(top, f, (t_block *)block, block->a, bMass, fout);
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief API for calculation of centers of mass/geometry.
+ *
+ * This header defines a few functions that can be used to calculate
+ * centers of mass/geometry for a group of atoms.
+ * These routines can be used independently of the other parts of the
+ * library, but they are also used internally by the selection engine.
+ * In most cases, it should not be necessary to call these functions
+ * directly.
+ * Instead, one should write an analysis tool such that it gets all
+ * positions through selections.
+ *
+ * The functions in the header can be divided into a few groups based on the
+ * parameters they take. The simplest group of functions calculates the center
+ * of a single group of atoms:
+ * - gmx_calc_cog(): Calculates the center of geometry (COG) of a given
+ * group of atoms.
+ * - gmx_calc_com(): Calculates the center of mass (COM) of a given group
+ * of atoms.
+ * - gmx_calc_comg(): Calculates either the COM or COG, based on a
+ * gmx_boolean flag.
+ *
+ * A second set of routines is provided for calculating the centers for groups
+ * that wrap over periodic boundaries (gmx_calc_cog_pbc(), gmx_calc_com_pbc(),
+ * gmx_calc_comg_pbc()). These functions are slower, because they need to
+ * adjust the center iteratively.
+ *
+ * It is also possible to calculate centers for several groups of atoms in
+ * one call. The functions gmx_calc_cog_block(), gmx_calc_com_block() and
+ * gmx_calc_comg_block() take an index group and a partitioning of that index
+ * group (as a \c t_block structure), and calculate the centers for
+ * each group defined by the \c t_block structure separately.
+ *
+ * Finally, there is a function gmx_calc_comg_blocka() that takes both the
+ * index group and the partitioning as a single \c t_blocka structure.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_CENTEROFMASS_H
+#define GMX_SELECTION_CENTEROFMASS_H
+
+#include "../legacyheaders/typedefs.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Calculate a single center of geometry. */
+int
+gmx_calc_cog(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout);
+/** Calculate a single center of mass. */
+int
+gmx_calc_com(t_topology *top, rvec x[], int nrefat, atom_id index[], rvec xout);
+/** Calculate force on a single center of geometry. */
+int
+gmx_calc_cog_f(t_topology *top, rvec f[], int nrefat, atom_id index[], rvec fout);
+/** Calculate a single center of mass/geometry. */
+int
+gmx_calc_comg(t_topology *top, rvec x[], int nrefat, atom_id index[],
+ gmx_bool bMass, rvec xout);
+/** Calculate force on a single center of mass/geometry. */
+int
+gmx_calc_comg_f(t_topology *top, rvec f[], int nrefat, atom_id index[],
+ gmx_bool bMass, rvec fout);
+
+/** Calculate a single center of geometry iteratively, taking PBC into account. */
+int
+gmx_calc_cog_pbc(t_topology *top, rvec x[], t_pbc *pbc,
+ int nrefat, atom_id index[], rvec xout);
+/** Calculate a single center of mass iteratively, taking PBC into account. */
+int
+gmx_calc_com_pbc(t_topology *top, rvec x[], t_pbc *pbc,
+ int nrefat, atom_id index[], rvec xout);
+/** Calculate a single center of mass/geometry iteratively with PBC. */
+int
+gmx_calc_comg_pbc(t_topology *top, rvec x[], t_pbc *pbc,
+ int nrefat, atom_id index[], gmx_bool bMass, rvec xout);
+
+/** Calculate centers of geometry for a blocked index. */
+int
+gmx_calc_cog_block(t_topology *top, rvec x[], t_block *block,
+ atom_id index[], rvec xout[]);
+/** Calculate centers of mass for a blocked index. */
+int
+gmx_calc_com_block(t_topology *top, rvec x[], t_block *block,
+ atom_id index[], rvec xout[]);
+/** Calculate forces on centers of geometry for a blocked index. */
+int
+gmx_calc_cog_f_block(t_topology *top, rvec f[], t_block *block,
+ atom_id index[], rvec fout[]);
+/** Calculate centers of mass/geometry for a blocked index. */
+int
+gmx_calc_comg_block(t_topology *top, rvec x[], t_block *block,
+ atom_id index[], gmx_bool bMass, rvec xout[]);
+/** Calculate forces on centers of mass/geometry for a blocked index. */
+int
+gmx_calc_comg_f_block(t_topology *top, rvec f[], t_block *block,
+ atom_id index[], gmx_bool bMass, rvec fout[]);
+/** Calculate centers of mass/geometry for a set of blocks; */
+int
+gmx_calc_comg_blocka(t_topology *top, rvec x[], t_blocka *block,
+ gmx_bool bMass, rvec xout[]);
+/** Calculate forces on centers of mass/geometry for a set of blocks; */
+int
+gmx_calc_comg_f_blocka(t_topology *top, rvec x[], t_blocka *block,
+ gmx_bool bMass, rvec xout[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Selection compilation and optimization.
+ *
+ * \todo
+ * Better error handling and memory management in error situations.
+ * At least, the main compilation function leaves the selection collection in
+ * a bad state if an error occurs.
+ *
+ * \todo
+ * The memory usage could still be optimized.
+ * Use of memory pooling could still be extended, and a lot of redundant
+ * gmin/gmax data could be eliminated for complex arithmetic expressions.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+/*! \internal
+ * \page page_module_selection_compiler Selection compilation
+ *
+ * The compiler takes the selection element tree from the selection parser
+ * (see \ref page_module_selection_parser) as input.
+ * The selection parser is quite independent of selection evaluation details,
+ * and the compiler processes the tree to conform to what the evaluation
+ * functions expect.
+ * For better control and optimization possibilities, the compilation is
+ * done on all selections simultaneously.
+ * Hence, all the selections should be parsed before the compiler can be
+ * called.
+ *
+ * The compiler initializes all fields in \c t_selelem not initialized by
+ * the parser: \c t_selelem::v (some fields have already been initialized by
+ * the parser), \c t_selelem::evaluate, and \c t_selelem::u (again, some
+ * elements have been initialized in the parser).
+ * The \c t_selelem::cdata field is used during the compilation to store
+ * internal data, but the data is freed when the compiler returns.
+ *
+ * In addition to initializing the elements, the compiler reorganizes the tree
+ * to simplify and optimize evaluation. The compiler also evaluates the static
+ * parts of the selection: in the end of the compilation, static parts have
+ * been replaced by the result of the evaluation.
+ *
+ * The compiler is called by calling gmx_ana_selcollection_compile().
+ * This functions then does the compilation in several passes over the
+ * \c t_selelem tree.
+ * -# Defaults are set for the position type and flags of position calculation
+ * methods that were not explicitly specified in the user input.
+ * -# Subexpressions are extracted: a separate root is created for each
+ * subexpression, and placed before the expression is first used.
+ * Currently, only variables and expressions used to evaluate parameter
+ * values are extracted, but common subexpression could also be detected
+ * here.
+ * -# A second pass with simple reordering and initialization is done:
+ * -# Boolean expressions are combined such that one element can evaluate,
+ * e.g., "A and B and C". The subexpressions in gmx_boolean expression are
+ * reordered such that static expressions come first without otherwise
+ * altering the relative order of the expressions.
+ * -# The \c t_selelem::evaluate field is set to the correct evaluation
+ * function from evaluate.h.
+ * -# The compiler data structure is allocated for each element, and
+ * the fields are initialized, with the exception of the contents of
+ * \c gmax and \c gmin fields. In reality, several passes are made
+ * to completely initialize the structure, because some flags are set
+ * recursively based on which elements refer to an element, and these
+ * flags need to be set to initialize other fields.
+ * .
+ * -# The evaluation function of all elements is replaced with the
+ * analyze_static() function to be able to initialize the element before
+ * the actual evaluation function is called.
+ * The evaluation machinery is then called to initialize the whole tree,
+ * while simultaneously evaluating the static expressions.
+ * During the evaluation, track is kept of the smallest and largest
+ * possible selections, and these are stored in the internal compiler
+ * data structure for each element.
+ * To be able to do this for all possible values of dynamical expressions,
+ * special care needs to be taken with gmx_boolean expressions because they
+ * are short-circuiting. This is done through the
+ * \c SEL_CDATA_EVALMAX flag, which makes dynamic child expressions
+ * of \c BOOL_OR expressions evaluate to empty groups, while subexpressions
+ * of \c BOOL_AND are evaluated to largest possible groups.
+ * Memory is also allocated to store the results of the evaluation.
+ * For each element, analyze_static() calls the actual evaluation function
+ * after the element has been properly initialized.
+ * -# Another evaluation pass is done over subexpressions with more than
+ * one reference to them. These cannot be completely processed during the
+ * first pass, because it is not known whether later references require
+ * additional evaluation of static expressions.
+ * -# Unused subexpressions are removed. For efficiency reasons (and to avoid
+ * some checks), this is actually done several times already earlier in
+ * the compilation process.
+ * -# Most of the processing is now done, and the next pass simply sets the
+ * evaluation group of root elements to the largest selection as determined
+ * in pass 4. For root elements of subexpressions that should not be
+ * evaluated before they are referred to, the evaluation group/function is
+ * cleared. At the same time, position calculation data is initialized for
+ * for selection method elements that require it. Compiler data is also
+ * freed as it is no longer needed.
+ * -# A final pass initializes the total masses and charges in the
+ * \c gmx_ana_selection_t data structures.
+ *
+ * The actual evaluation of the selection is described in the documentation
+ * of the functions in evaluate.h.
+ *
+ * \todo
+ * Some combinations of method parameter flags are not yet properly treated by
+ * the compiler or the evaluation functions in evaluate.cpp. All the ones used by
+ * currently implemented methods should work, but new combinations might not.
+ *
+ *
+ * \section selcompiler_tree Element tree after compilation
+ *
+ * After the compilation, the selection element tree is suitable for
+ * gmx_ana_selcollection_evaluate().
+ * Enough memory has been allocated for \ref t_selelem::v
+ * (and \ref t_selelem::cgrp for \ref SEL_SUBEXPR elements) to allow the
+ * selection to be evaluated without allocating any memory.
+ *
+ *
+ * \subsection selcompiler_tree_root Root elements
+ *
+ * The top level of the tree consists of a chain of \ref SEL_ROOT elements.
+ * These are used for two purposes:
+ * -# A selection that should be evaluated.
+ * These elements appear in the same order as the selections in the input.
+ * For these elements, \ref t_selelem::v has been set to the maximum
+ * possible group that the selection can evaluate to (only for dynamic
+ * selections), and \ref t_selelem::cgrp has been set to use a NULL group
+ * for evaluation.
+ * -# A subexpression that appears in one or more selections.
+ * Each selection that gives a value for a method parameter is a
+ * potential subexpression, as is any variable value.
+ * Only subexpressions that require evaluation for each frame are left
+ * after the selection is compiled.
+ * Each subexpression appears in the chain before any references to it.
+ * For these elements, \c t_selelem::cgrp has been set to the group
+ * that should be used to evaluate the subexpression.
+ * If \c t_selelem::cgrp is empty, the total evaluation group is not known
+ * in advance or it is more efficient to evaluate the subexpression only
+ * when it is referenced. If this is the case, \c t_selelem::evaluate is
+ * also NULL.
+ *
+ * The children of the \ref SEL_ROOT elements can be used to distinguish
+ * the two types of root elements from each other; the rules are the same
+ * as for the parsed tree (see \ref selparser_tree_root).
+ * Subexpressions are treated as if they had been provided through variables.
+ *
+ * Selection names are stored as after parsing (see \ref selparser_tree_root).
+ *
+ *
+ * \subsection selcompiler_tree_const Constant elements
+ *
+ * All (sub)selections that do not require particle positions have been
+ * replaced with \ref SEL_CONST elements.
+ * Constant elements from the parser are also retained if present in
+ * dynamic parts of the selections.
+ * Several constant elements with a NULL \c t_selelem::evaluate are left for
+ * debugging purposes; of these, only the ones for \ref BOOL_OR expressions are
+ * used during evaluation.
+ *
+ * The value is stored in \c t_selelem::v, and for group values with an
+ * evaluation function set, also in \c t_selelem::cgrp.
+ * For \ref GROUP_VALUE elements, unnecessary atoms (i.e., atoms that
+ * could never be selected) have been removed from the value.
+ *
+ * \ref SEL_CONST elements have no children.
+ *
+ *
+ * \subsection selcompiler_tree_method Method evaluation elements
+ *
+ * All selection methods that need to be evaluated dynamically are described
+ * by a \ref SEL_EXPRESSION element. The \c t_selelem::method and
+ * \c t_selelem::mdata fields have already been initialized by the parser,
+ * and the compiler only calls the initialization functions in the method
+ * data structure to do some additional initialization of these fields at
+ * appropriate points. If the \c t_selelem::pc data field has been created by
+ * the parser, the compiler initializes the data structure properly once the
+ * required positions are known. If the \c t_selelem::pc field is NULL after
+ * the parser, but the method provides only sel_updatefunc_pos(), an
+ * appropriate position calculation data structure is created.
+ * If \c t_selelem::pc is not NULL, \c t_selelem::pos is also initialized
+ * to hold the positions calculated.
+ *
+ * Children of these elements are of type \ref SEL_SUBEXPRREF, and describe
+ * parameter values that need to be evaluated for each frame. See the next
+ * section for more details.
+ * \ref SEL_CONST children can also appear, and stand for parameters that get
+ * their value from a static expression. These elements are present only for
+ * debugging purposes: they always have a NULL evaluation function.
+ *
+ *
+ * \subsection selcompiler_tree_subexpr Subexpression elements
+ *
+ * As described in \ref selcompiler_tree_root, subexpressions are created
+ * for each variable and each expression that gives a value to a selection
+ * method parameter. As the only child of the \ref SEL_ROOT element,
+ * these elements have a \ref SEL_SUBEXPR element. The \ref SEL_SUBEXPR
+ * element has a single child, which evaluates the actual expression.
+ * After compilation, only subexpressions that require particle positions
+ * for evaluation are left.
+ * For non-variable subexpression, automatic names have been generated to
+ * help in debugging.
+ *
+ * For \ref SEL_SUBEXPR elements, memory has been allocated for
+ * \c t_selelem::cgrp to store the group for which the expression has been
+ * evaluated during the current frame. This is only done if full subexpression
+ * evaluation by _gmx_sel_evaluate_subexpr() is needed; the other evaluation
+ * functions do not require this memory.
+ *
+ * \ref SEL_SUBEXPRREF elements are used to describe references to
+ * subexpressions. They have always a single child, which is the
+ * \ref SEL_SUBEXPR element being referenced.
+ *
+ * If a subexpression is used only once, the evaluation has been optimized by
+ * setting the child of the \ref SEL_SUBEXPR element to evaluate the value of
+ * \ref SEL_SUBEXPRREF directly (in the case of memory pooling, this is managed
+ * by the evaluation functions). In such cases, the evaluation routines for the
+ * \ref SEL_SUBEXPRREF and \ref SEL_SUBEXPR elements only propagate some status
+ * information, but do not unnecessarily copy the values.
+ *
+ *
+ * \subsection selcompiler_tree_gmx_bool Boolean elements
+ *
+ * \ref SEL_BOOLEAN elements have been merged such that one element
+ * may carry out evaluation of more than one operation of the same type.
+ * The static parts of the expressions have been evaluated, and are placed
+ * in the first child. These are followed by the dynamic expressions, in the
+ * order provided by the user.
+ *
+ *
+ * \subsection selcompiler_tree_arith Arithmetic elements
+ *
+ * Constant and static expressions in \ref SEL_ARITHMETIC elements have been
+ * calculated.
+ * Currently, no other processing is done.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdarg.h>
+
+#include <smalloc.h>
+#include <string2.h>
+#include <vec.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "evaluate.h"
+#include "keywords.h"
+#include "mempool.h"
+#include "selectioncollection-impl.h"
+#include "selelem.h"
+
+static int min(int a, int b)
+{
+ return (a < b) ? a : b;
+}
+
+/*! \internal \brief
+ * Compiler flags.
+ */
+enum
+{
+ /*! \brief
+ * Whether a subexpression needs to evaluated for all atoms.
+ *
+ * This flag is set for \ref SEL_SUBEXPR elements that are used to
+ * evaluate non-atom-valued selection method parameters, as well as
+ * those that are used directly as values of selections.
+ */
+ SEL_CDATA_FULLEVAL = 1,
+ /*! \brief
+ * Whether the whole subexpression should be treated as static.
+ *
+ * This flag is always FALSE if \ref SEL_DYNAMIC is set for the element,
+ * but it is also FALSE for static elements within common subexpressions.
+ */
+ SEL_CDATA_STATIC = 2,
+ /** Whether the subexpression will always be evaluated in the same group. */
+ SEL_CDATA_STATICEVAL = 4,
+ /** Whether the compiler evaluation routine should return the maximal selection. */
+ SEL_CDATA_EVALMAX = 8,
+ /** Whether memory has been allocated for \p gmin and \p gmax. */
+ SEL_CDATA_MINMAXALLOC = 16,
+ /** Whether subexpressions use simple pass evaluation functions. */
+ SEL_CDATA_SIMPLESUBEXPR = 32,
+ /** Whether this expressions is a part of a common subexpression. */
+ SEL_CDATA_COMMONSUBEXPR = 64
+};
+
+/*! \internal \brief
+ * Internal data structure used by the compiler.
+ */
+typedef struct t_compiler_data
+{
+ /** The real evaluation method. */
+ sel_evalfunc evaluate;
+ /** Flags for specifying how to treat this element during compilation. */
+ int flags;
+ /** Smallest selection that can be selected by the subexpression. */
+ gmx_ana_index_t *gmin;
+ /** Largest selection that can be selected by the subexpression. */
+ gmx_ana_index_t *gmax;
+} t_compiler_data;
+
+
+/********************************************************************
+ * COMPILER UTILITY FUNCTIONS
+ ********************************************************************/
+
+static void
+print_group_info(FILE *fp, const char *name, t_selelem *sel, gmx_ana_index_t *g)
+{
+ fprintf(fp, " %s=", name);
+ if (!g)
+ {
+ fprintf(fp, "(null)");
+ }
+ else if (sel->cdata->flags & SEL_CDATA_MINMAXALLOC)
+ {
+ fprintf(fp, "(%d atoms, %p)", g->isize, (void*)g);
+ }
+ else if (sel->v.type == GROUP_VALUE && g == sel->v.u.g)
+ {
+ fprintf(fp, "(static, %p)", (void*)g);
+ }
+ else
+ {
+ fprintf(fp, "%p", (void*)g);
+ }
+}
+
+/*!
+ * \param[in] fp File handle to receive the output.
+ * \param[in] sel Selection element to print.
+ * \param[in] level Indentation level, starting from zero.
+ */
+void
+_gmx_selelem_print_compiler_info(FILE *fp, t_selelem *sel, int level)
+{
+ if (!sel->cdata)
+ {
+ return;
+ }
+ fprintf(fp, "%*c cdata: flg=", level*2+1, ' ');
+ if (sel->cdata->flags & SEL_CDATA_FULLEVAL)
+ {
+ fprintf(fp, "F");
+ }
+ if (!(sel->cdata->flags & SEL_CDATA_STATIC))
+ {
+ fprintf(fp, "D");
+ }
+ if (sel->cdata->flags & SEL_CDATA_STATICEVAL)
+ {
+ fprintf(fp, "S");
+ }
+ if (sel->cdata->flags & SEL_CDATA_EVALMAX)
+ {
+ fprintf(fp, "M");
+ }
+ if (sel->cdata->flags & SEL_CDATA_MINMAXALLOC)
+ {
+ fprintf(fp, "A");
+ }
+ if (sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
+ {
+ fprintf(fp, "Ss");
+ }
+ if (sel->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
+ {
+ fprintf(fp, "Sc");
+ }
+ if (!sel->cdata->flags)
+ {
+ fprintf(fp, "0");
+ }
+ fprintf(fp, " eval=");
+ _gmx_sel_print_evalfunc_name(fp, sel->cdata->evaluate);
+ print_group_info(fp, "gmin", sel, sel->cdata->gmin);
+ print_group_info(fp, "gmax", sel, sel->cdata->gmax);
+ fprintf(fp, "\n");
+}
+
+/*!
+ * \param sel Selection to free.
+ *
+ * This function only frees the data for the given selection, not its children.
+ * It is safe to call the function when compiler data has not been allocated
+ * or has already been freed; in such a case, nothing is done.
+ */
+void
+_gmx_selelem_free_compiler_data(t_selelem *sel)
+{
+ if (sel->cdata)
+ {
+ sel->evaluate = sel->cdata->evaluate;
+ if (sel->cdata->flags & SEL_CDATA_MINMAXALLOC)
+ {
+ sel->cdata->gmin->name = NULL;
+ sel->cdata->gmax->name = NULL;
+ gmx_ana_index_deinit(sel->cdata->gmin);
+ gmx_ana_index_deinit(sel->cdata->gmax);
+ sfree(sel->cdata->gmin);
+ sfree(sel->cdata->gmax);
+ }
+ sfree(sel->cdata);
+ }
+ sel->cdata = NULL;
+}
+
+/*! \brief
+ * Allocates memory for storing the evaluated value of a selection element.
+ *
+ * \param sel Selection element to initialize
+ * \param[in] isize Maximum evaluation group size.
+ * \param[in] bChildEval TRUE if children have already been processed.
+ * \returns TRUE if the memory was allocated, FALSE if children need to
+ * be processed first.
+ *
+ * If called more than once, memory is (re)allocated to ensure that the
+ * maximum of the \p isize values can be stored.
+ */
+static gmx_bool
+alloc_selection_data(t_selelem *sel, int isize, gmx_bool bChildEval)
+{
+ int nalloc;
+
+ if (sel->mempool)
+ {
+ return TRUE;
+ }
+ /* Find out the number of elements to allocate */
+ if (sel->flags & SEL_SINGLEVAL)
+ {
+ nalloc = 1;
+ }
+ else if (sel->flags & SEL_ATOMVAL)
+ {
+ nalloc = isize;
+ }
+ else /* sel->flags should contain SEL_VARNUMVAL */
+ {
+ t_selelem *child;
+
+ if (!bChildEval)
+ {
+ return FALSE;
+ }
+ child = (sel->type == SEL_SUBEXPRREF ? sel->child : sel);
+ if (child->type == SEL_SUBEXPR)
+ {
+ child = child->child;
+ }
+ nalloc = (sel->v.type == POS_VALUE) ? child->v.u.p->nr : child->v.nr;
+ }
+ /* For positions, we actually want to allocate just a single structure
+ * for nalloc positions. */
+ if (sel->v.type == POS_VALUE)
+ {
+ isize = nalloc;
+ nalloc = 1;
+ }
+ /* Allocate memory for sel->v.u if needed */
+ if (sel->flags & SEL_ALLOCVAL)
+ {
+ _gmx_selvalue_reserve(&sel->v, nalloc);
+ }
+ /* Reserve memory inside group and position structures if
+ * SEL_ALLOCDATA is set. */
+ if (sel->flags & SEL_ALLOCDATA)
+ {
+ if (sel->v.type == GROUP_VALUE)
+ {
+ gmx_ana_index_reserve(sel->v.u.g, isize);
+ }
+ else if (sel->v.type == POS_VALUE)
+ {
+ gmx_ana_pos_reserve(sel->v.u.p, isize, 0);
+ }
+ }
+ return TRUE;
+}
+
+/*! \brief
+ * Replace the evaluation function of each element in the subtree.
+ *
+ * \param sel Root of the selection subtree to process.
+ * \param[in] eval The new evaluation function.
+ */
+static void
+set_evaluation_function(t_selelem *sel, sel_evalfunc eval)
+{
+ sel->evaluate = eval;
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ t_selelem *child = sel->child;
+ while (child)
+ {
+ set_evaluation_function(child, eval);
+ child = child->next;
+ }
+ }
+}
+
+
+/********************************************************************
+ * POSITION KEYWORD DEFAULT INITIALIZATION
+ ********************************************************************/
+
+/*! \brief
+ * Initializes default values for position keyword evaluation.
+ *
+ * \param[in,out] root Root of the element tree to initialize.
+ * \param[in] spost Default output position type.
+ * \param[in] rpost Default reference position type.
+ * \param[in] sel Selection that the element evaluates the positions
+ * for, or NULL if the element is an internal element.
+ */
+static void
+init_pos_keyword_defaults(t_selelem *root, const char *spost,
+ const char *rpost, const gmx::Selection *sel)
+{
+ /* Selections use largest static group by default, while
+ * reference positions use the whole residue/molecule. */
+ if (root->type == SEL_EXPRESSION)
+ {
+ bool bSelection = (sel != NULL);
+ int flags = bSelection ? POS_COMPLMAX : POS_COMPLWHOLE;
+ if (bSelection)
+ {
+ if (sel->hasFlag(gmx::efDynamicMask))
+ {
+ flags |= POS_MASKONLY;
+ }
+ if (sel->hasFlag(gmx::efEvaluateVelocities))
+ {
+ flags |= POS_VELOCITIES;
+ }
+ if (sel->hasFlag(gmx::efEvaluateForces))
+ {
+ flags |= POS_FORCES;
+ }
+ }
+ _gmx_selelem_set_kwpos_type(root, bSelection ? spost : rpost);
+ _gmx_selelem_set_kwpos_flags(root, flags);
+ }
+ /* Change the defaults once we are no longer processing modifiers */
+ if (root->type != SEL_ROOT && root->type != SEL_MODIFIER
+ && root->type != SEL_SUBEXPRREF && root->type != SEL_SUBEXPR)
+ {
+ sel = NULL;
+ }
+ /* Recurse into children */
+ t_selelem *child = root->child;
+ while (child)
+ {
+ init_pos_keyword_defaults(child, spost, rpost, sel);
+ child = child->next;
+ }
+}
+
+
+/********************************************************************
+ * SUBEXPRESSION PROCESSING
+ ********************************************************************/
+
+/*! \brief
+ * Reverses the chain of selection elements starting at \p root.
+ *
+ * \param root First selection in the whole selection chain.
+ * \returns The new first element for the chain.
+ */
+static t_selelem *
+reverse_selelem_chain(t_selelem *root)
+{
+ t_selelem *item;
+ t_selelem *prev;
+ t_selelem *next;
+
+ prev = NULL;
+ item = root;
+ while (item)
+ {
+ next = item->next;
+ item->next = prev;
+ prev = item;
+ item = next;
+ }
+ return prev;
+}
+
+/*! \brief
+ * Removes subexpressions that don't have any references.
+ *
+ * \param root First selection in the whole selection chain.
+ * \returns The new first element for the chain.
+ *
+ * The elements are processed in reverse order to correctly detect
+ * subexpressions only referred to by other subexpressions.
+ */
+static t_selelem *
+remove_unused_subexpressions(t_selelem *root)
+{
+ t_selelem *item;
+ t_selelem *prev;
+ t_selelem *next;
+
+ if (root == NULL)
+ {
+ return NULL;
+ }
+ root = reverse_selelem_chain(root);
+ while (root->child->type == SEL_SUBEXPR && root->child->refcount == 1)
+ {
+ next = root->next;
+ _gmx_selelem_free(root);
+ root = next;
+ }
+ prev = root;
+ item = root->next;
+ while (item)
+ {
+ next = item->next;
+ if (item->child->type == SEL_SUBEXPR && item->child->refcount == 1)
+ {
+ prev->next = next;
+ _gmx_selelem_free(item);
+ }
+ else
+ {
+ prev = item;
+ }
+ item = next;
+ }
+ return reverse_selelem_chain(root);
+}
+
+/*! \brief
+ * Creates a name with a running number for a subexpression.
+ *
+ * \param[in,out] sel The subexpression to be named.
+ * \param[in] i Running number for the subexpression.
+ *
+ * The name of the selection becomes "SubExpr N", where N is \p i;
+ * Memory is allocated for the name and the name is stored both in
+ * \c t_selelem::name and \c t_selelem::u::cgrp::name; the latter
+ * is freed by _gmx_selelem_free().
+ */
+static void
+create_subexpression_name(t_selelem *sel, int i)
+{
+ int len, ret;
+ char *name;
+
+ len = 8 + (int)log10(abs(i)) + 3;
+ snew(name, len+1);
+ /* FIXME: snprintf used to be used here for extra safety, but this
+ * requires extra checking on Windows since it only provides a
+ * non-C99-conforming implementation as _snprintf()... */
+ ret = sprintf(name, "SubExpr %d", i);
+ if (ret < 0 || ret > len)
+ {
+ sfree(name);
+ name = NULL;
+ }
+ sel->name = name;
+ sel->u.cgrp.name = name;
+}
+
+/*! \brief
+ * Processes and extracts subexpressions from a given selection subtree.
+ *
+ * \param sel Root of the subtree to process.
+ * \param subexprn Pointer to a subexpression counter.
+ * \returns Pointer to a chain of subselections, or NULL if none were found.
+ *
+ * This function finds recursively all \ref SEL_SUBEXPRREF elements below
+ * the given root element and ensures that their children are within
+ * \ref SEL_SUBEXPR elements. It also creates a chain of \ref SEL_ROOT elements
+ * that contain the subexpression as their children and returns the first
+ * of these root elements.
+ */
+static t_selelem *
+extract_item_subselections(t_selelem *sel, int *subexprn)
+{
+ t_selelem *root;
+ t_selelem *subexpr;
+ t_selelem *child;
+
+ root = subexpr = NULL;
+ child = sel->child;
+ while (child)
+ {
+ if (!root)
+ {
+ root = subexpr = extract_item_subselections(child, subexprn);
+ }
+ else
+ {
+ subexpr->next = extract_item_subselections(child, subexprn);
+ }
+ while (subexpr && subexpr->next)
+ {
+ subexpr = subexpr->next;
+ }
+ /* The latter check excludes variable references.
+ * It also excludes subexpression elements that have already been
+ * processed, because they are given a name when they are first
+ * encountered.
+ * TODO: There should be a more robust mechanism (probably a dedicated
+ * flag) for detecting parser-generated subexpressions than relying on
+ * a NULL name field. */
+ if (child->type == SEL_SUBEXPRREF && (child->child->type != SEL_SUBEXPR
+ || child->child->name == NULL))
+ {
+ /* Create the root element for the subexpression */
+ if (!root)
+ {
+ root = subexpr = _gmx_selelem_create(SEL_ROOT);
+ }
+ else
+ {
+ subexpr->next = _gmx_selelem_create(SEL_ROOT);
+ subexpr = subexpr->next;
+ }
+ /* Create the subexpression element and/or
+ * move the actual subexpression under the created element. */
+ if (child->child->type != SEL_SUBEXPR)
+ {
+ subexpr->child = _gmx_selelem_create(SEL_SUBEXPR);
+ _gmx_selelem_set_vtype(subexpr->child, child->v.type);
+ subexpr->child->child = child->child;
+ child->child = subexpr->child;
+ }
+ else
+ {
+ subexpr->child = child->child;
+ }
+ create_subexpression_name(subexpr->child, ++*subexprn);
+ subexpr->child->refcount++;
+ /* Set the flags for the created elements */
+ subexpr->flags |= (child->flags & SEL_VALFLAGMASK);
+ subexpr->child->flags |= (child->flags & SEL_VALFLAGMASK);
+ }
+ child = child->next;
+ }
+
+ return root;
+}
+
+/*! \brief
+ * Extracts subexpressions of the selection chain.
+ *
+ * \param sel First selection in the whole selection chain.
+ * \returns The new first element for the chain.
+ *
+ * Finds all the subexpressions (and their subexpressions) in the
+ * selection chain starting from \p sel and creates \ref SEL_SUBEXPR
+ * elements for them.
+ * \ref SEL_ROOT elements are also created for each subexpression
+ * and inserted into the selection chain before the expressions that
+ * refer to them.
+ */
+static t_selelem *
+extract_subexpressions(t_selelem *sel)
+{
+ t_selelem *root, *item, *next;
+ int subexprn;
+
+ subexprn = 0;
+ root = NULL;
+ next = sel;
+ while (next)
+ {
+ item = extract_item_subselections(next, &subexprn);
+ if (item)
+ {
+ if (!root)
+ {
+ root = item;
+ }
+ else
+ {
+ sel->next = item;
+ }
+ while (item->next)
+ {
+ item = item->next;
+ }
+ item->next = next;
+ }
+ else if (!root)
+ {
+ root = next;
+ }
+ sel = next;
+ next = next->next;
+ }
+ return root;
+}
+
+
+/********************************************************************
+ * BOOLEAN OPERATION REORDERING
+ ********************************************************************/
+
+/*! \brief
+ * Removes redundant gmx_boolean selection elements.
+ *
+ * \param sel Root of the selection subtree to optimize.
+ *
+ * This function merges similar gmx_boolean operations (e.g., (A or B) or C becomes
+ * a single OR operation with three operands).
+ */
+static void
+optimize_gmx_boolean_expressions(t_selelem *sel)
+{
+ t_selelem *child, *prev;
+
+ /* Do recursively for children */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ prev = NULL;
+ child = sel->child;
+ while (child)
+ {
+ optimize_gmx_boolean_expressions(child);
+ /* Remove double negations */
+ if (child->type == SEL_BOOLEAN && child->u.boolt == BOOL_NOT
+ && child->child->type == SEL_BOOLEAN && child->child->u.boolt == BOOL_NOT)
+ {
+ /* Move the doubly negated expression up two levels */
+ if (!prev)
+ {
+ sel->child = child->child->child;
+ prev = sel->child;
+ }
+ else
+ {
+ prev->next = child->child->child;
+ prev = prev->next;
+ }
+ child->child->child->next = child->next;
+ /* Remove the two negations */
+ child->child->child = NULL;
+ child->next = NULL;
+ _gmx_selelem_free(child);
+ child = prev;
+ }
+ prev = child;
+ child = child->next;
+ }
+ }
+ if (sel->type != SEL_BOOLEAN || sel->u.boolt == BOOL_NOT)
+ {
+ return;
+ }
+ /* Merge subsequent binary operations */
+ prev = NULL;
+ child = sel->child;
+ while (child)
+ {
+ if (child->type == SEL_BOOLEAN && child->u.boolt == sel->u.boolt)
+ {
+ if (!prev)
+ {
+ sel->child = child->child;
+ prev = sel->child;
+ }
+ else
+ {
+ prev->next = child->child;
+ }
+ while (prev->next)
+ {
+ prev = prev->next;
+ }
+ prev->next = child->next;
+ sfree(child->v.u.g);
+ sfree(child);
+ child = prev->next;
+ }
+ else
+ {
+ prev = child;
+ child = child->next;
+ }
+ }
+}
+
+/*! \brief
+ * Reorders children of gmx_boolean expressions such that static selections
+ * come first.
+ *
+ * \param sel Root of the selection subtree to reorder.
+ *
+ * The relative order of static expressions does not change.
+ * The same is true for the dynamic expressions.
+ */
+static void
+reorder_gmx_boolean_static_children(t_selelem *sel)
+{
+ t_selelem *child, *prev, *next;
+
+ /* Do recursively for children */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ child = sel->child;
+ while (child)
+ {
+ reorder_gmx_boolean_static_children(child);
+ child = child->next;
+ }
+ }
+
+ /* Reorder gmx_boolean expressions such that static selections come first */
+ if (sel->type == SEL_BOOLEAN && (sel->flags & SEL_DYNAMIC))
+ {
+ t_selelem start;
+
+ start.next = sel->child;
+ prev = &start;
+ child = &start;
+ while (child->next)
+ {
+ /* child is the last handled static expression */
+ /* prev is the last handled non-static expression */
+ next = prev->next;
+ while (next && (next->flags & SEL_DYNAMIC))
+ {
+ prev = next;
+ next = next->next;
+ }
+ /* next is now the first static expression after child */
+ if (!next)
+ {
+ break;
+ }
+ /* Reorder such that next comes after child */
+ if (prev != child)
+ {
+ prev->next = next->next;
+ next->next = child->next;
+ child->next = next;
+ }
+ else
+ {
+ prev = prev->next;
+ }
+ /* Advance child by one */
+ child = next;
+ }
+
+ sel->child = start.next;
+ }
+}
+
+
+/********************************************************************
+ * ARITHMETIC EXPRESSION PROCESSING
+ ********************************************************************/
+
+/*! \brief
+ * Processes arithmetic expressions to simplify and speed up evaluation.
+ *
+ * \param sel Root of the selection subtree to process.
+ *
+ * Currently, this function only converts integer constants to reals
+ * within arithmetic expressions.
+ */
+static gmx_bool
+optimize_arithmetic_expressions(t_selelem *sel)
+{
+ t_selelem *child;
+ gmx_bool bOk;
+
+ /* Do recursively for children. */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ child = sel->child;
+ while (child)
+ {
+ bOk = optimize_arithmetic_expressions(child);
+ if (!bOk)
+ {
+ return bOk;
+ }
+ child = child->next;
+ }
+ }
+
+ if (sel->type != SEL_ARITHMETIC)
+ {
+ return TRUE;
+ }
+
+ /* Convert integer constants to reals. */
+ child = sel->child;
+ while (child)
+ {
+ if (child->v.type == INT_VALUE)
+ {
+ real *r;
+
+ if (child->type != SEL_CONST)
+ {
+ gmx_impl("Non-constant integer expressions not implemented in arithmetic evaluation");
+ return FALSE;
+ }
+ snew(r, 1);
+ r[0] = child->v.u.i[0];
+ sfree(child->v.u.i);
+ child->v.u.r = r;
+ child->v.type = REAL_VALUE;
+ }
+ else if (child->v.type != REAL_VALUE)
+ {
+ gmx_bug("Internal error");
+ return FALSE;
+ }
+ child = child->next;
+ }
+ return TRUE;
+}
+
+
+/********************************************************************
+ * EVALUATION PREPARATION COMPILER
+ ********************************************************************/
+
+/*! \brief
+ * Sets the evaluation functions for the selection (sub)tree.
+ *
+ * \param[in,out] sel Root of the selection subtree to process.
+ * \returns TRUE on success, FALSE if any subexpression fails.
+ *
+ * This function sets the evaluation function (\c t_selelem::evaluate)
+ * for the selection elements.
+ */
+static gmx_bool
+init_item_evalfunc(t_selelem *sel)
+{
+ /* Process children. */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ t_selelem *child;
+
+ child = sel->child;
+ while (child)
+ {
+ if (!init_item_evalfunc(child))
+ {
+ return FALSE;
+ }
+ child = child->next;
+ }
+ }
+
+ /* Set the evaluation function */
+ switch (sel->type)
+ {
+ case SEL_CONST:
+ if (sel->v.type == GROUP_VALUE)
+ {
+ sel->evaluate = &_gmx_sel_evaluate_static;
+ }
+ break;
+
+ case SEL_EXPRESSION:
+ if (!(sel->flags & SEL_DYNAMIC) && sel->u.expr.method
+ && sel->u.expr.method->init_frame)
+ {
+ sel->flags |= SEL_INITFRAME;
+ }
+ sel->evaluate = &_gmx_sel_evaluate_method;
+ break;
+
+ case SEL_ARITHMETIC:
+ sel->evaluate = &_gmx_sel_evaluate_arithmetic;
+ break;
+
+ case SEL_MODIFIER:
+ if (sel->v.type != NO_VALUE)
+ {
+ sel->evaluate = &_gmx_sel_evaluate_modifier;
+ }
+ break;
+
+ case SEL_BOOLEAN:
+ switch (sel->u.boolt)
+ {
+ case BOOL_NOT: sel->evaluate = &_gmx_sel_evaluate_not; break;
+ case BOOL_AND: sel->evaluate = &_gmx_sel_evaluate_and; break;
+ case BOOL_OR: sel->evaluate = &_gmx_sel_evaluate_or; break;
+ case BOOL_XOR:
+ gmx_impl("xor expressions not implemented");
+ return FALSE;
+ }
+ break;
+
+ case SEL_ROOT:
+ sel->evaluate = &_gmx_sel_evaluate_root;
+ break;
+
+ case SEL_SUBEXPR:
+ sel->evaluate = (sel->refcount == 2
+ ? &_gmx_sel_evaluate_subexpr_simple
+ : &_gmx_sel_evaluate_subexpr);
+ break;
+
+ case SEL_SUBEXPRREF:
+ sel->name = sel->child->name;
+ sel->evaluate = (sel->child->refcount == 2
+ ? &_gmx_sel_evaluate_subexprref_simple
+ : &_gmx_sel_evaluate_subexprref);
+ break;
+
+ case SEL_GROUPREF:
+ gmx_incons("unresolved group reference in compilation");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*! \brief
+ * Sets the memory pool for selection elements that can use it.
+ *
+ * \param sel Root of the selection subtree to process.
+ * \param[in] mempool Memory pool to use.
+ */
+static void
+setup_memory_pooling(t_selelem *sel, gmx_sel_mempool_t *mempool)
+{
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ t_selelem *child;
+
+ child = sel->child;
+ while (child)
+ {
+ if ((sel->type == SEL_BOOLEAN && (child->flags & SEL_DYNAMIC))
+ || (sel->type == SEL_ARITHMETIC && child->type != SEL_CONST
+ && !(child->flags & SEL_SINGLEVAL))
+ || (sel->type == SEL_SUBEXPR && sel->refcount > 2))
+ {
+ child->mempool = mempool;
+ if (child->type == SEL_SUBEXPRREF
+ && child->child->refcount == 2)
+ {
+ child->child->child->mempool = mempool;
+ }
+ }
+ setup_memory_pooling(child, mempool);
+ child = child->next;
+ }
+ }
+}
+
+/*! \brief
+ * Prepares the selection (sub)tree for evaluation.
+ *
+ * \param[in,out] sel Root of the selection subtree to prepare.
+ *
+ * It also allocates memory for the \p sel->v.u.g or \p sel->v.u.p
+ * structure if required.
+ */
+static void
+init_item_evaloutput(t_selelem *sel)
+{
+ /* Process children. */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ t_selelem *child;
+
+ child = sel->child;
+ while (child)
+ {
+ init_item_evaloutput(child);
+ child = child->next;
+ }
+ }
+
+ if (sel->type == SEL_SUBEXPR && sel->refcount == 2)
+ {
+ sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
+ {
+ _gmx_selvalue_setstore(&sel->v, sel->child->v.u.ptr);
+ }
+ }
+ else if (sel->type == SEL_SUBEXPR
+ && (sel->cdata->flags & SEL_CDATA_FULLEVAL))
+ {
+ sel->evaluate = &_gmx_sel_evaluate_subexpr_staticeval;
+ sel->cdata->evaluate = sel->evaluate;
+ sel->child->mempool = NULL;
+ sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
+ {
+ _gmx_selvalue_setstore(&sel->v, sel->child->v.u.ptr);
+ }
+ }
+ else if (sel->type == SEL_SUBEXPRREF && sel->child->refcount == 2)
+ {
+ if (sel->v.u.ptr)
+ {
+ _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
+ _gmx_selelem_free_values(sel->child->child);
+ sel->child->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ sel->child->child->flags |= (sel->flags & SEL_ALLOCDATA);
+ _gmx_selvalue_setstore(&sel->child->child->v, sel->v.u.ptr);
+ }
+ else if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
+ {
+ _gmx_selvalue_setstore(&sel->v, sel->child->child->v.u.ptr);
+ }
+ sel->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ }
+
+ /* Make sure that the group/position structure is allocated. */
+ if (!sel->v.u.ptr && (sel->flags & SEL_ALLOCVAL))
+ {
+ if (sel->v.type == GROUP_VALUE || sel->v.type == POS_VALUE)
+ {
+ _gmx_selvalue_reserve(&sel->v, 1);
+ sel->v.nr = 1;
+ }
+ }
+}
+
+
+/********************************************************************
+ * COMPILER DATA INITIALIZATION
+ ********************************************************************/
+
+/*! \brief
+ * Allocates memory for the compiler data and initializes the structure.
+ *
+ * \param sel Root of the selection subtree to process.
+ */
+static void
+init_item_compilerdata(t_selelem *sel)
+{
+ t_selelem *child;
+
+ /* Allocate the compiler data structure */
+ snew(sel->cdata, 1);
+
+ /* Store the real evaluation method because the compiler will replace it */
+ sel->cdata->evaluate = sel->evaluate;
+
+ /* Initialize the flags */
+ sel->cdata->flags = SEL_CDATA_STATICEVAL;
+ if (!(sel->flags & SEL_DYNAMIC))
+ {
+ sel->cdata->flags |= SEL_CDATA_STATIC;
+ }
+ if (sel->type == SEL_SUBEXPR)
+ {
+ sel->cdata->flags |= SEL_CDATA_EVALMAX;
+ }
+ /* Set the full evaluation flag for subexpressions that require it;
+ * the subexpression has already been initialized, so we can simply
+ * access its compilation flags.*/
+ if (sel->type == SEL_EXPRESSION || sel->type == SEL_MODIFIER)
+ {
+ child = sel->child;
+ while (child)
+ {
+ if (!(child->flags & SEL_ATOMVAL) && child->child)
+ {
+ child->child->cdata->flags |= SEL_CDATA_FULLEVAL;
+ }
+ child = child->next;
+ }
+ }
+ else if (sel->type == SEL_ROOT && sel->child->type == SEL_SUBEXPRREF)
+ {
+ sel->child->child->cdata->flags |= SEL_CDATA_FULLEVAL;
+ }
+
+ /* Initialize children */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ child = sel->child;
+ while (child)
+ {
+ init_item_compilerdata(child);
+ child = child->next;
+ }
+ }
+
+ /* Determine whether we should evaluate the minimum or the maximum
+ * for the children of this element. */
+ if (sel->type == SEL_BOOLEAN)
+ {
+ gmx_bool bEvalMax;
+
+ bEvalMax = (sel->u.boolt == BOOL_AND);
+ child = sel->child;
+ while (child)
+ {
+ if (bEvalMax)
+ {
+ child->cdata->flags |= SEL_CDATA_EVALMAX;
+ }
+ else if (child->type == SEL_BOOLEAN && child->u.boolt == BOOL_NOT)
+ {
+ child->child->cdata->flags |= SEL_CDATA_EVALMAX;
+ }
+ child = child->next;
+ }
+ }
+ else if (sel->type == SEL_EXPRESSION || sel->type == SEL_MODIFIER
+ || sel->type == SEL_SUBEXPR)
+ {
+ child = sel->child;
+ while (child)
+ {
+ child->cdata->flags |= SEL_CDATA_EVALMAX;
+ child = child->next;
+ }
+ }
+}
+
+/*! \brief
+ * Initializes the static evaluation flag for a selection subtree.
+ *
+ * \param[in,out] sel Root of the selection subtree to process.
+ *
+ * Sets the \c bStaticEval in the compiler data structure:
+ * for any element for which the evaluation group may depend on the trajectory
+ * frame, the flag is cleared.
+ *
+ * reorder_gmx_boolean_static_children() should have been called.
+ */
+static void
+init_item_staticeval(t_selelem *sel)
+{
+ t_selelem *child;
+
+ /* Subexpressions with full evaluation should always have bStaticEval,
+ * so don't do anything if a reference to them is encountered. */
+ if (sel->type == SEL_SUBEXPRREF
+ && (sel->child->cdata->flags & SEL_CDATA_FULLEVAL))
+ {
+ return;
+ }
+
+ /* Propagate the bStaticEval flag to children if it is not set */
+ if (!(sel->cdata->flags & SEL_CDATA_STATICEVAL))
+ {
+ child = sel->child;
+ while (child)
+ {
+ if ((sel->type != SEL_EXPRESSION && sel->type != SEL_MODIFIER)
+ || (child->flags & SEL_ATOMVAL))
+ {
+ if (child->cdata->flags & SEL_CDATA_STATICEVAL)
+ {
+ child->cdata->flags &= ~SEL_CDATA_STATICEVAL;
+ init_item_staticeval(child);
+ }
+ }
+ child = child->next;
+ }
+ }
+ else /* bStaticEval is set */
+ {
+ /* For gmx_boolean expressions, any expression after the first dynamic
+ * expression should not have bStaticEval. */
+ if (sel->type == SEL_BOOLEAN)
+ {
+ child = sel->child;
+ while (child && !(child->flags & SEL_DYNAMIC))
+ {
+ child = child->next;
+ }
+ if (child)
+ {
+ child = child->next;
+ }
+ while (child)
+ {
+ child->cdata->flags &= ~SEL_CDATA_STATICEVAL;
+ child = child->next;
+ }
+ }
+
+ /* Process the children */
+ child = sel->child;
+ while (child)
+ {
+ init_item_staticeval(child);
+ child = child->next;
+ }
+ }
+}
+
+/*! \brief
+ * Initializes compiler flags for subexpressions.
+ *
+ * \param sel Root of the selection subtree to process.
+ */
+static void
+init_item_subexpr_flags(t_selelem *sel)
+{
+ if (sel->type == SEL_SUBEXPR)
+ {
+ if (sel->refcount == 2)
+ {
+ sel->cdata->flags |= SEL_CDATA_SIMPLESUBEXPR;
+ }
+ else if (!(sel->cdata->flags & SEL_CDATA_FULLEVAL))
+ {
+ sel->cdata->flags |= SEL_CDATA_COMMONSUBEXPR;
+ }
+ }
+ else if (sel->type == SEL_SUBEXPRREF && sel->child->refcount == 2)
+ {
+ sel->cdata->flags |= SEL_CDATA_SIMPLESUBEXPR;
+ }
+
+ /* Process children, but only follow subexpression references if the
+ * common subexpression flag needs to be propagated. */
+ if (sel->type != SEL_SUBEXPRREF
+ || ((sel->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
+ && sel->child->refcount > 2))
+ {
+ t_selelem *child = sel->child;
+
+ while (child)
+ {
+ if (!(child->cdata->flags & SEL_CDATA_COMMONSUBEXPR))
+ {
+ if (sel->type != SEL_EXPRESSION || (child->flags & SEL_ATOMVAL))
+ {
+ child->cdata->flags |=
+ (sel->cdata->flags & SEL_CDATA_COMMONSUBEXPR);
+ }
+ init_item_subexpr_flags(child);
+ }
+ child = child->next;
+ }
+ }
+}
+
+/*! \brief
+ * Initializes the gmin and gmax fields of the compiler data structure.
+ *
+ * \param sel Root of the selection subtree to process.
+ */
+static void
+init_item_minmax_groups(t_selelem *sel)
+{
+ /* Process children. */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ t_selelem *child;
+
+ child = sel->child;
+ while (child)
+ {
+ init_item_minmax_groups(child);
+ child = child->next;
+ }
+ }
+
+ /* Initialize the minimum and maximum evaluation groups. */
+ if (sel->type != SEL_ROOT && sel->v.type != NO_VALUE)
+ {
+ if (sel->v.type == GROUP_VALUE
+ && (sel->cdata->flags & SEL_CDATA_STATIC))
+ {
+ sel->cdata->gmin = sel->v.u.g;
+ sel->cdata->gmax = sel->v.u.g;
+ }
+ else if (sel->type == SEL_SUBEXPR
+ && ((sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
+ || (sel->cdata->flags & SEL_CDATA_FULLEVAL)))
+ {
+ sel->cdata->gmin = sel->child->cdata->gmin;
+ sel->cdata->gmax = sel->child->cdata->gmax;
+ }
+ else
+ {
+ sel->cdata->flags |= SEL_CDATA_MINMAXALLOC;
+ snew(sel->cdata->gmin, 1);
+ snew(sel->cdata->gmax, 1);
+ }
+ }
+}
+
+
+/********************************************************************
+ * EVALUATION GROUP INITIALIZATION
+ ********************************************************************/
+
+/*! \brief
+ * Initializes evaluation groups for root items.
+ *
+ * \param[in,out] sc Selection collection data.
+ *
+ * The evaluation group of each \ref SEL_ROOT element corresponding to a
+ * selection in \p sc is set to \p gall. The same is done for \ref SEL_ROOT
+ * elements corresponding to subexpressions that need full evaluation.
+ */
+static void
+initialize_evalgrps(gmx_ana_selcollection_t *sc)
+{
+ t_selelem *root;
+
+ root = sc->root;
+ while (root)
+ {
+ if (root->child->type != SEL_SUBEXPR
+ || (root->child->cdata->flags & SEL_CDATA_FULLEVAL))
+ {
+ gmx_ana_index_set(&root->u.cgrp, sc->gall.isize, sc->gall.index,
+ root->u.cgrp.name, 0);
+ }
+ root = root->next;
+ }
+}
+
+
+/********************************************************************
+ * STATIC ANALYSIS
+ ********************************************************************/
+
+/*! \brief
+ * Marks a subtree completely dynamic or undoes such a change.
+ *
+ * \param sel Selection subtree to mark.
+ * \param[in] bDynamic If TRUE, the \p bStatic flag of the whole
+ * selection subtree is cleared. If FALSE, the flag is restored to
+ * using \ref SEL_DYNAMIC.
+ *
+ * Does not descend into parameters of methods unless the parameters
+ * are evaluated for each atom.
+ */
+static void
+mark_subexpr_dynamic(t_selelem *sel, gmx_bool bDynamic)
+{
+ t_selelem *child;
+
+ if (!bDynamic && !(sel->flags & SEL_DYNAMIC))
+ {
+ sel->cdata->flags |= SEL_CDATA_STATIC;
+ }
+ else
+ {
+ sel->cdata->flags &= ~SEL_CDATA_STATIC;
+ }
+ child = sel->child;
+ while (child)
+ {
+ if (sel->type != SEL_EXPRESSION || child->type != SEL_SUBEXPRREF
+ || (child->u.param->flags & SPAR_ATOMVAL))
+ {
+ mark_subexpr_dynamic(child, bDynamic);
+ }
+ child = child->next;
+ }
+}
+
+/*! \brief
+ * Frees memory for subexpressions that are no longer needed.
+ *
+ * \param sel Selection subtree to check.
+ *
+ * Checks whether the subtree rooted at \p sel refers to any \ref SEL_SUBEXPR
+ * elements that are not referred to by anything else except their own root
+ * element. If such elements are found, all memory allocated for them is freed
+ * except the actual element. The element is left because otherwise a dangling
+ * pointer would be left at the root element, which is not traversed by this
+ * function. Later compilation passes remove the stub elements.
+ */
+static void
+release_subexpr_memory(t_selelem *sel)
+{
+ if (sel->type == SEL_SUBEXPR)
+ {
+ if (sel->refcount == 2)
+ {
+ release_subexpr_memory(sel->child);
+ sel->name = NULL;
+ _gmx_selelem_free_chain(sel->child);
+ _gmx_selelem_free_values(sel);
+ _gmx_selelem_free_exprdata(sel);
+ _gmx_selelem_free_compiler_data(sel);
+ sel->child = NULL;
+ }
+ }
+ else
+ {
+ t_selelem *child;
+
+ child = sel->child;
+ while (child)
+ {
+ release_subexpr_memory(child);
+ child = child->next;
+ }
+ }
+}
+
+/*! \brief
+ * Makes an evaluated selection element static.
+ *
+ * \param sel Selection element to make static.
+ *
+ * The evaluated value becomes the value of the static element.
+ * The element type is changed to SEL_CONST and the children are
+ * deleted.
+ */
+static void
+make_static(t_selelem *sel)
+{
+ /* If this is a subexpression reference and the data is stored in the
+ * child, we transfer data ownership before doing anything else. */
+ if (sel->type == SEL_SUBEXPRREF
+ && (sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR))
+ {
+ if (sel->child->child->flags & SEL_ALLOCDATA)
+ {
+ sel->flags |= SEL_ALLOCDATA;
+ sel->child->child->flags &= ~SEL_ALLOCDATA;
+ }
+ if (sel->child->child->flags & SEL_ALLOCVAL)
+ {
+ sel->flags |= SEL_ALLOCVAL;
+ sel->v.nalloc = sel->child->child->v.nalloc;
+ sel->child->child->flags &= ~SEL_ALLOCVAL;
+ sel->child->child->v.nalloc = -1;
+ }
+ }
+ /* Free the children. */
+ release_subexpr_memory(sel);
+ _gmx_selelem_free_chain(sel->child);
+ sel->child = NULL;
+ /* Free the expression data as it is no longer needed */
+ _gmx_selelem_free_exprdata(sel);
+ /* Make the item static */
+ sel->name = NULL;
+ sel->type = SEL_CONST;
+ sel->evaluate = NULL;
+ sel->cdata->evaluate = NULL;
+ /* Set the group value.
+ * free_exprdata above frees the cgrp group, so we can just override it. */
+ if (sel->v.type == GROUP_VALUE)
+ {
+ gmx_ana_index_set(&sel->u.cgrp, sel->v.u.g->isize, sel->v.u.g->index, NULL, 0);
+ }
+}
+
+/*! \brief
+ * Evaluates a constant expression during analyze_static().
+ *
+ * \param[in] data Evaluation data.
+ * \param[in,out] sel Selection to process.
+ * \param[in] g The evaluation group.
+ * \returns 0 on success, a non-zero error code on error.
+ */
+static int
+process_const(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ int rc;
+
+ rc = 0;
+ if (sel->v.type == GROUP_VALUE)
+ {
+ if (sel->cdata->evaluate)
+ {
+ rc = sel->cdata->evaluate(data, sel, g);
+ }
+ }
+ /* Other constant expressions do not need evaluation */
+ return rc;
+}
+
+/*! \brief
+ * Sets the parameter value pointer for \ref SEL_SUBEXPRREF params.
+ *
+ * \param[in,out] sel Selection to process.
+ *
+ * Copies the value pointer of \p sel to \c sel->u.param if one is present
+ * and should receive the value from the compiler
+ * (most parameter values are handled during parsing).
+ * If \p sel is not of type \ref SEL_SUBEXPRREF, or if \c sel->u.param is NULL,
+ * the function does nothing.
+ * Also, if the \c sel->u.param does not have \ref SPAR_VARNUM or
+ * \ref SPAR_ATOMVAL, the function returns immediately.
+ */
+static void
+store_param_val(t_selelem *sel)
+{
+ /* Return immediately if there is no parameter. */
+ if (sel->type != SEL_SUBEXPRREF || !sel->u.param)
+ {
+ return;
+ }
+
+ /* Or if the value does not need storing. */
+ if (!(sel->u.param->flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
+ {
+ return;
+ }
+
+ if (sel->v.type == INT_VALUE || sel->v.type == REAL_VALUE
+ || sel->v.type == STR_VALUE)
+ {
+ _gmx_selvalue_setstore(&sel->u.param->val, sel->v.u.ptr);
+ }
+}
+
+/*! \brief
+ * Handles the initialization of a selection method during analyze_static() pass.
+ *
+ * \param[in,out] sel Selection element to process.
+ * \param[in] top Topology structure.
+ * \param[in] isize Size of the evaluation group for the element.
+ * \returns 0 on success, a non-zero error code on return.
+ *
+ * Calls sel_initfunc() (and possibly sel_outinitfunc()) to initialize the
+ * method.
+ * If no \ref SPAR_ATOMVAL parameters are present, multiple initialization
+ * is prevented by using \ref SEL_METHODINIT and \ref SEL_OUTINIT flags.
+ */
+static int
+init_method(t_selelem *sel, t_topology *top, int isize)
+{
+ t_selelem *child;
+ gmx_bool bAtomVal;
+ int rc;
+
+ /* Find out whether there are any atom-valued parameters */
+ bAtomVal = FALSE;
+ child = sel->child;
+ while (child)
+ {
+ if (child->flags & SEL_ATOMVAL)
+ {
+ bAtomVal = TRUE;
+ }
+ child = child->next;
+ }
+
+ /* Initialize the method */
+ if (sel->u.expr.method->init
+ && (bAtomVal || !(sel->flags & SEL_METHODINIT)))
+ {
+ sel->flags |= SEL_METHODINIT;
+ rc = sel->u.expr.method->init(top, sel->u.expr.method->nparams,
+ sel->u.expr.method->param, sel->u.expr.mdata);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (bAtomVal || !(sel->flags & SEL_OUTINIT))
+ {
+ sel->flags |= SEL_OUTINIT;
+ if (sel->u.expr.method->outinit)
+ {
+ rc = sel->u.expr.method->outinit(top, &sel->v, sel->u.expr.mdata);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if (sel->v.type != POS_VALUE && sel->v.type != GROUP_VALUE)
+ {
+ alloc_selection_data(sel, isize, TRUE);
+ }
+ }
+ else
+ {
+ alloc_selection_data(sel, isize, TRUE);
+ if ((sel->flags & SEL_DYNAMIC)
+ && sel->v.type != GROUP_VALUE && sel->v.type != POS_VALUE)
+ {
+ sel->v.nr = isize;
+ }
+ /* If the method is char-valued, pre-allocate the strings. */
+ if (sel->u.expr.method->flags & SMETH_CHARVAL)
+ {
+ int i;
+
+ /* A sanity check */
+ if (sel->v.type != STR_VALUE)
+ {
+ gmx_bug("internal error");
+ return -1;
+ }
+ sel->flags |= SEL_ALLOCDATA;
+ for (i = 0; i < isize; ++i)
+ {
+ if (sel->v.u.s[i] == NULL)
+ {
+ snew(sel->v.u.s[i], 2);
+ }
+ }
+ }
+ }
+ /* Clear the values for dynamic output to avoid valgrind warnings. */
+ if ((sel->flags & SEL_DYNAMIC) && sel->v.type == REAL_VALUE)
+ {
+ int i;
+
+ for (i = 0; i < sel->v.nr; ++i)
+ {
+ sel->v.u.r[i] = 0.0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief
+ * Evaluates the static part of a gmx_boolean expression.
+ *
+ * \param[in] data Evaluation data.
+ * \param[in,out] sel Boolean selection element whose children should be
+ * processed.
+ * \param[in] g The evaluation group.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * reorder_item_static_children() should have been called.
+ */
+static int
+evaluate_gmx_boolean_static_part(gmx_sel_evaluate_t *data, t_selelem *sel,
+ gmx_ana_index_t *g)
+{
+ t_selelem *child, *next;
+ int rc;
+
+ /* Find the last static subexpression */
+ child = sel->child;
+ while (child->next && (child->next->cdata->flags & SEL_CDATA_STATIC))
+ {
+ child = child->next;
+ }
+ if (!(child->cdata->flags & SEL_CDATA_STATIC))
+ {
+ return 0;
+ }
+
+ /* Evalute the static part if there is more than one expression */
+ if (child != sel->child)
+ {
+ next = child->next;
+ child->next = NULL;
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ /* Replace the subexpressions with the result */
+ _gmx_selelem_free_chain(sel->child);
+ snew(child, 1);
+ child->type = SEL_CONST;
+ child->flags = SEL_FLAGSSET | SEL_SINGLEVAL | SEL_ALLOCVAL | SEL_ALLOCDATA;
+ _gmx_selelem_set_vtype(child, GROUP_VALUE);
+ child->evaluate = NULL;
+ _gmx_selvalue_reserve(&child->v, 1);
+ gmx_ana_index_copy(child->v.u.g, sel->v.u.g, TRUE);
+ init_item_compilerdata(child);
+ init_item_minmax_groups(child);
+ child->cdata->flags &= ~SEL_CDATA_STATICEVAL;
+ child->cdata->flags |= sel->cdata->flags & SEL_CDATA_STATICEVAL;
+ child->next = next;
+ sel->child = child;
+ }
+ else if (child->evaluate)
+ {
+ rc = child->evaluate(data, child, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ /* Set the evaluation function for the constant element.
+ * We never need to evaluate the element again during compilation,
+ * but we may need to evaluate the static part again if the
+ * expression is not an OR with a static evaluation group.
+ * If we reach here with a NOT expression, the NOT expression
+ * is also static, and will be made a constant later, so don't waste
+ * time copying the group. */
+ child->evaluate = NULL;
+ if (sel->u.boolt == BOOL_NOT
+ || ((sel->cdata->flags & SEL_CDATA_STATICEVAL)
+ && sel->u.boolt == BOOL_OR))
+ {
+ child->cdata->evaluate = NULL;
+ }
+ else
+ {
+ child->cdata->evaluate = &_gmx_sel_evaluate_static;
+ /* The cgrp has only been allocated if it originated from an
+ * external index group. In that case, we need special handling
+ * to preserve the name of the group and to not leak memory.
+ * If cgrp has been set in make_static(), it is not allocated,
+ * and hence we can overwrite it safely. */
+ if (child->u.cgrp.nalloc_index > 0)
+ {
+ char *name = child->u.cgrp.name;
+ gmx_ana_index_copy(&child->u.cgrp, child->v.u.g, FALSE);
+ gmx_ana_index_squeeze(&child->u.cgrp);
+ child->u.cgrp.name = name;
+ }
+ else
+ {
+ gmx_ana_index_copy(&child->u.cgrp, child->v.u.g, TRUE);
+ }
+ }
+ return 0;
+}
+
+/*! \brief
+ * Evaluates the minimum and maximum groups for a gmx_boolean expression.
+ *
+ * \param[in] sel \ref SEL_BOOLEAN element currently being evaluated.
+ * \param[in] g Group for which \p sel has been evaluated.
+ * \param[out] gmin Largest subset of the possible values of \p sel.
+ * \param[out] gmax Smallest superset of the possible values of \p sel.
+ *
+ * This is a helper function for analyze_static() that is called for
+ * dynamic \ref SEL_BOOLEAN elements after they have been evaluated.
+ * It uses the minimum and maximum groups of the children to calculate
+ * the minimum and maximum groups for \p sel, and also updates the static
+ * part of \p sel (which is in the first child) if the children give
+ * cause for this.
+ *
+ * This function may allocate some extra memory for \p gmin and \p gmax,
+ * but as these groups are freed at the end of analyze_static() (which is
+ * reached shortly after this function returns), this should not be a major
+ * problem.
+ */
+static void
+evaluate_gmx_boolean_minmax_grps(t_selelem *sel, gmx_ana_index_t *g,
+ gmx_ana_index_t *gmin, gmx_ana_index_t *gmax)
+{
+ t_selelem *child;
+
+ switch (sel->u.boolt)
+ {
+ case BOOL_NOT:
+ gmx_ana_index_reserve(gmin, g->isize);
+ gmx_ana_index_reserve(gmax, g->isize);
+ gmx_ana_index_difference(gmax, g, sel->child->cdata->gmin);
+ gmx_ana_index_difference(gmin, g, sel->child->cdata->gmax);
+ break;
+
+ case BOOL_AND:
+ gmx_ana_index_copy(gmin, sel->child->cdata->gmin, TRUE);
+ gmx_ana_index_copy(gmax, sel->child->cdata->gmax, TRUE);
+ child = sel->child->next;
+ while (child && gmax->isize > 0)
+ {
+ gmx_ana_index_intersection(gmin, gmin, child->cdata->gmin);
+ gmx_ana_index_intersection(gmax, gmax, child->cdata->gmax);
+ child = child->next;
+ }
+ /* Update the static part if other expressions limit it */
+ if ((sel->child->cdata->flags & SEL_CDATA_STATIC)
+ && sel->child->v.u.g->isize > gmax->isize)
+ {
+ gmx_ana_index_copy(sel->child->v.u.g, gmax, FALSE);
+ gmx_ana_index_squeeze(sel->child->v.u.g);
+ if (sel->child->u.cgrp.isize > 0)
+ {
+ gmx_ana_index_copy(&sel->child->u.cgrp, gmax, FALSE);
+ gmx_ana_index_squeeze(&sel->child->u.cgrp);
+ }
+ }
+ break;
+
+ case BOOL_OR:
+ /* We can assume here that the gmin of children do not overlap
+ * because of the way _gmx_sel_evaluate_or() works. */
+ gmx_ana_index_reserve(gmin, g->isize);
+ gmx_ana_index_reserve(gmax, g->isize);
+ gmx_ana_index_copy(gmin, sel->child->cdata->gmin, FALSE);
+ gmx_ana_index_copy(gmax, sel->child->cdata->gmax, FALSE);
+ child = sel->child->next;
+ while (child && gmin->isize < g->isize)
+ {
+ gmx_ana_index_merge(gmin, gmin, child->cdata->gmin);
+ gmx_ana_index_union(gmax, gmax, child->cdata->gmax);
+ child = child->next;
+ }
+ /* Update the static part if other expressions have static parts
+ * that are not included. */
+ if ((sel->child->cdata->flags & SEL_CDATA_STATIC)
+ && sel->child->v.u.g->isize < gmin->isize)
+ {
+ gmx_ana_index_reserve(sel->child->v.u.g, gmin->isize);
+ gmx_ana_index_copy(sel->child->v.u.g, gmin, FALSE);
+ if (sel->child->u.cgrp.isize > 0)
+ {
+ gmx_ana_index_reserve(&sel->child->u.cgrp, gmin->isize);
+ gmx_ana_index_copy(&sel->child->u.cgrp, gmin, FALSE);
+ }
+ }
+ break;
+
+ case BOOL_XOR: /* Should not be reached */
+ gmx_impl("xor expressions not implemented");
+ break;
+ }
+}
+
+/*! \brief
+ * Evaluates the static parts of \p sel and analyzes the structure.
+ *
+ * \param[in] data Evaluation data.
+ * \param[in,out] sel Selection currently being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * This function is used as the replacement for the \c t_selelem::evaluate
+ * function pointer.
+ * It does the single most complex task in the compiler: after all elements
+ * have been processed, the \p gmin and \p gmax fields of \p t_compiler_data
+ * have been properly initialized, enough memory has been allocated for
+ * storing the value of each expression, and the static parts of the
+ * expressions have been evaluated.
+ * The above is exactly true only for elements other than subexpressions:
+ * another pass is required for subexpressions that are referred to more than
+ * once and whose evaluation group is not known in advance.
+ */
+static int
+analyze_static(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ t_selelem *child, *next;
+ gmx_bool bDoMinMax;
+ int rc;
+
+ if (sel->type != SEL_ROOT && g)
+ {
+ alloc_selection_data(sel, g->isize, FALSE);
+ }
+
+ bDoMinMax = (sel->cdata->flags & SEL_CDATA_MINMAXALLOC);
+ if (sel->type != SEL_SUBEXPR && bDoMinMax)
+ {
+ gmx_ana_index_deinit(sel->cdata->gmin);
+ gmx_ana_index_deinit(sel->cdata->gmax);
+ }
+
+ /* TODO: This switch is awfully long... */
+ rc = 0;
+ switch (sel->type)
+ {
+ case SEL_CONST:
+ rc = process_const(data, sel, g);
+ break;
+
+ case SEL_EXPRESSION:
+ case SEL_MODIFIER:
+ rc = _gmx_sel_evaluate_method_params(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = init_method(sel, data->top, g->isize);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if (!(sel->flags & SEL_DYNAMIC))
+ {
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (rc == 0 && (sel->cdata->flags & SEL_CDATA_STATIC))
+ {
+ make_static(sel);
+ }
+ }
+ else
+ {
+ /* Modifiers need to be evaluated even though they process
+ * positions to get the modified output groups from the
+ * maximum possible selections. */
+ if (sel->type == SEL_MODIFIER)
+ {
+ rc = sel->cdata->evaluate(data, sel, g);
+ }
+ if (bDoMinMax)
+ {
+ gmx_ana_index_copy(sel->cdata->gmax, g, TRUE);
+ }
+ }
+ break;
+
+ case SEL_BOOLEAN:
+ if (!(sel->flags & SEL_DYNAMIC))
+ {
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (rc == 0 && (sel->cdata->flags & SEL_CDATA_STATIC))
+ {
+ make_static(sel);
+ }
+ }
+ else
+ {
+ /* Evalute the static part if there is more than one expression */
+ rc = evaluate_gmx_boolean_static_part(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ /* Evaluate the selection.
+ * If the type is gmx_boolean, we must explicitly handle the
+ * static part evaluated in evaluate_gmx_boolean_static_part()
+ * here because g may be larger. */
+ if (sel->u.boolt == BOOL_AND && sel->child->type == SEL_CONST)
+ {
+ rc = sel->cdata->evaluate(data, sel, sel->child->v.u.g);
+ }
+ else
+ {
+ rc = sel->cdata->evaluate(data, sel, g);
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ /* Evaluate minimal and maximal selections */
+ evaluate_gmx_boolean_minmax_grps(sel, g, sel->cdata->gmin,
+ sel->cdata->gmax);
+ }
+ break;
+
+ case SEL_ARITHMETIC:
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if (!(sel->flags & SEL_DYNAMIC))
+ {
+ if (sel->cdata->flags & SEL_CDATA_STATIC)
+ {
+ make_static(sel);
+ }
+ }
+ else if (bDoMinMax)
+ {
+ gmx_ana_index_copy(sel->cdata->gmax, g, TRUE);
+ }
+ break;
+
+ case SEL_ROOT:
+ rc = sel->cdata->evaluate(data, sel, g);
+ break;
+
+ case SEL_SUBEXPR:
+ if (sel->cdata->flags & (SEL_CDATA_SIMPLESUBEXPR | SEL_CDATA_FULLEVAL))
+ {
+ rc = sel->cdata->evaluate(data, sel, g);
+ _gmx_selvalue_setstore(&sel->v, sel->child->v.u.ptr);
+ }
+ else if (sel->u.cgrp.isize == 0)
+ {
+ gmx_ana_index_reserve(&sel->u.cgrp, g->isize);
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (bDoMinMax)
+ {
+ gmx_ana_index_copy(sel->cdata->gmin, sel->child->cdata->gmin, TRUE);
+ gmx_ana_index_copy(sel->cdata->gmax, sel->child->cdata->gmax, TRUE);
+ }
+ }
+ else
+ {
+ int isize = gmx_ana_index_difference_size(g, &sel->u.cgrp);
+ if (isize > 0)
+ {
+ isize += sel->u.cgrp.isize;
+ gmx_ana_index_reserve(&sel->u.cgrp, isize);
+ alloc_selection_data(sel, isize, FALSE);
+ }
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (isize > 0 && bDoMinMax)
+ {
+ gmx_ana_index_reserve(sel->cdata->gmin,
+ sel->cdata->gmin->isize
+ + sel->child->cdata->gmin->isize);
+ gmx_ana_index_reserve(sel->cdata->gmax,
+ sel->cdata->gmax->isize
+ + sel->child->cdata->gmax->isize);
+ gmx_ana_index_merge(sel->cdata->gmin, sel->cdata->gmin,
+ sel->child->cdata->gmin);
+ gmx_ana_index_merge(sel->cdata->gmax, sel->cdata->gmax,
+ sel->child->cdata->gmax);
+ }
+ }
+ break;
+
+ case SEL_SUBEXPRREF:
+ if (!g && !(sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR))
+ {
+ /* The subexpression should have been evaluated if g is NULL
+ * (i.e., this is a method parameter or a direct value of a
+ * selection). */
+ alloc_selection_data(sel, sel->child->cdata->gmax->isize, TRUE);
+ }
+ rc = sel->cdata->evaluate(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if ((sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
+ && (sel->child->child->flags & SEL_ALLOCVAL))
+ {
+ _gmx_selvalue_setstore(&sel->v, sel->child->child->v.u.ptr);
+ }
+ /* Store the parameter value if required */
+ store_param_val(sel);
+ if (!(sel->flags & SEL_DYNAMIC))
+ {
+ if (sel->cdata->flags & SEL_CDATA_STATIC)
+ {
+ make_static(sel);
+ }
+ }
+ else if (bDoMinMax)
+ {
+ if ((sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR) || !g)
+ {
+ gmx_ana_index_copy(sel->cdata->gmin, sel->child->cdata->gmin, TRUE);
+ gmx_ana_index_copy(sel->cdata->gmax, sel->child->cdata->gmax, TRUE);
+ }
+ else
+ {
+ gmx_ana_index_reserve(sel->cdata->gmin,
+ min(g->isize, sel->child->cdata->gmin->isize));
+ gmx_ana_index_reserve(sel->cdata->gmax,
+ min(g->isize, sel->child->cdata->gmax->isize));
+ gmx_ana_index_intersection(sel->cdata->gmin,
+ sel->child->cdata->gmin, g);
+ gmx_ana_index_intersection(sel->cdata->gmax,
+ sel->child->cdata->gmax, g);
+ }
+ }
+ break;
+
+ case SEL_GROUPREF:
+ gmx_incons("unresolved group reference in compilation");
+ return -1;
+ }
+ /* Exit if there was some problem */
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ /* Update the minimal and maximal evaluation groups */
+ if (bDoMinMax)
+ {
+ gmx_ana_index_squeeze(sel->cdata->gmin);
+ gmx_ana_index_squeeze(sel->cdata->gmax);
+ sfree(sel->cdata->gmin->name);
+ sfree(sel->cdata->gmax->name);
+ sel->cdata->gmin->name = NULL;
+ sel->cdata->gmax->name = NULL;
+ }
+
+ /* Replace the result of the evaluation */
+ /* This is not necessary for subexpressions or for gmx_boolean negations
+ * because the evaluation function already has done it properly. */
+ if (sel->v.type == GROUP_VALUE && (sel->flags & SEL_DYNAMIC)
+ && sel->type != SEL_SUBEXPR
+ && !(sel->type == SEL_BOOLEAN && sel->u.boolt == BOOL_NOT))
+ {
+ if (sel->cdata->flags & SEL_CDATA_EVALMAX)
+ {
+ gmx_ana_index_copy(sel->v.u.g, sel->cdata->gmax, FALSE);
+ }
+ else
+ {
+ gmx_ana_index_copy(sel->v.u.g, sel->cdata->gmin, FALSE);
+ }
+ }
+
+ return 0;
+}
+
+
+/********************************************************************
+ * EVALUATION GROUP INITIALIZATION
+ ********************************************************************/
+
+/*! \brief
+ * Initializes the evaluation group for a \ref SEL_ROOT element.
+ *
+ * \param root Root element to initialize.
+ * \param[in] gall Group of all atoms.
+ *
+ * Checks whether it is necessary to evaluate anything through the root
+ * element, and either clears the evaluation function or initializes the
+ * evaluation group.
+ */
+static void
+init_root_item(t_selelem *root, gmx_ana_index_t *gall)
+{
+ t_selelem *expr;
+ char *name;
+
+ expr = root->child;
+ /* Subexpressions with non-static evaluation group should not be
+ * evaluated by the root, and neither should be single-reference
+ * subexpressions that don't evaluate for all atoms. */
+ if (expr->type == SEL_SUBEXPR
+ && (!(root->child->cdata->flags & SEL_CDATA_STATICEVAL)
+ || ((root->child->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
+ && !(root->child->cdata->flags & SEL_CDATA_FULLEVAL))))
+ {
+ root->evaluate = NULL;
+ if (root->cdata)
+ {
+ root->cdata->evaluate = NULL;
+ }
+ }
+
+ /* Set the evaluation group */
+ name = root->u.cgrp.name;
+ if (root->evaluate)
+ {
+ /* Non-atom-valued non-group expressions don't care about the group, so
+ * don't allocate any memory for it. */
+ if ((expr->flags & SEL_VARNUMVAL)
+ || ((expr->flags & SEL_SINGLEVAL) && expr->v.type != GROUP_VALUE))
+ {
+ gmx_ana_index_set(&root->u.cgrp, -1, NULL, NULL, 0);
+ }
+ else if (expr->cdata->gmax->isize == gall->isize)
+ {
+ /* Save some memory by only referring to the global group. */
+ gmx_ana_index_set(&root->u.cgrp, gall->isize, gall->index, NULL, 0);
+ }
+ else
+ {
+ gmx_ana_index_copy(&root->u.cgrp, expr->cdata->gmax, TRUE);
+ }
+ /* For selections, store the maximum group for
+ * gmx_ana_selcollection_evaluate_fin() as the value of the root
+ * element (unused otherwise). */
+ if (expr->type != SEL_SUBEXPR && expr->v.u.p->g)
+ {
+ t_selelem *child = expr;
+
+ /* TODO: This code is copied from parsetree.c; it would be better
+ * to have this hardcoded only in one place. */
+ while (child->type == SEL_MODIFIER)
+ {
+ child = child->child;
+ if (child->type == SEL_SUBEXPRREF)
+ {
+ child = child->child->child;
+ }
+ }
+ if (child->type == SEL_SUBEXPRREF)
+ {
+ child = child->child->child;
+ }
+ if (child->child->flags & SEL_DYNAMIC)
+ {
+ _gmx_selelem_set_vtype(root, GROUP_VALUE);
+ root->flags |= (SEL_ALLOCVAL | SEL_ALLOCDATA);
+ _gmx_selvalue_reserve(&root->v, 1);
+ gmx_ana_index_copy(root->v.u.g, expr->v.u.p->g, TRUE);
+ }
+ }
+ }
+ else
+ {
+ gmx_ana_index_clear(&root->u.cgrp);
+ }
+ root->u.cgrp.name = name;
+}
+
+
+/********************************************************************
+ * FINAL SUBEXPRESSION OPTIMIZATION
+ ********************************************************************/
+
+/*! \brief
+ * Optimizes subexpression evaluation.
+ *
+ * \param sel Root of the selection subtree to process.
+ *
+ * Optimizes away some unnecessary evaluation of subexpressions that are only
+ * referenced once.
+ */
+static void
+postprocess_item_subexpressions(t_selelem *sel)
+{
+ /* Process children. */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ t_selelem *child;
+
+ child = sel->child;
+ while (child)
+ {
+ postprocess_item_subexpressions(child);
+ child = child->next;
+ }
+ }
+
+ /* Replace the evaluation function of statically evaluated subexpressions
+ * for which the static group was not known in advance. */
+ if (sel->type == SEL_SUBEXPR && sel->refcount > 2
+ && (sel->cdata->flags & SEL_CDATA_STATICEVAL)
+ && !(sel->cdata->flags & SEL_CDATA_FULLEVAL))
+ {
+ char *name;
+
+ /* We need to free memory allocated for the group, because it is no
+ * longer needed (and would be lost on next call to the evaluation
+ * function). But we need to preserve the name. */
+ name = sel->u.cgrp.name;
+ gmx_ana_index_deinit(&sel->u.cgrp);
+ sel->u.cgrp.name = name;
+
+ sel->evaluate = &_gmx_sel_evaluate_subexpr_staticeval;
+ if (sel->cdata)
+ {
+ sel->cdata->evaluate = sel->evaluate;
+ }
+ _gmx_selelem_free_values(sel->child);
+ sel->child->mempool = NULL;
+ _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
+ sel->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ }
+
+ /* Adjust memory allocation flags for subexpressions that are used only
+ * once. This is not strictly necessary, but we do it to have the memory
+ * managed consistently for all types of subexpressions. */
+ if (sel->type == SEL_SUBEXPRREF
+ && (sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR))
+ {
+ if (sel->child->child->flags & SEL_ALLOCVAL)
+ {
+ sel->flags |= SEL_ALLOCVAL;
+ sel->flags |= (sel->child->child->flags & SEL_ALLOCDATA);
+ sel->v.nalloc = sel->child->child->v.nalloc;
+ sel->child->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ sel->child->child->v.nalloc = -1;
+ }
+ }
+
+ /* Do the same for subexpressions that are evaluated at once for all atoms. */
+ if (sel->type == SEL_SUBEXPR
+ && !(sel->cdata->flags & SEL_CDATA_SIMPLESUBEXPR)
+ && (sel->cdata->flags & SEL_CDATA_FULLEVAL))
+ {
+ sel->flags |= SEL_ALLOCVAL;
+ sel->flags |= (sel->child->flags & SEL_ALLOCDATA);
+ sel->v.nalloc = sel->child->v.nalloc;
+ sel->child->flags &= ~(SEL_ALLOCVAL | SEL_ALLOCDATA);
+ sel->child->v.nalloc = -1;
+ }
+}
+
+
+/********************************************************************
+ * COM CALCULATION INITIALIZATION
+ ********************************************************************/
+
+/*! \brief
+ * Initializes COM/COG calculation for method expressions that require it.
+ *
+ * \param sel Selection subtree to process.
+ * \param[in,out] pcc Position calculation collection to use.
+ * \param[in] type Default position calculation type.
+ * \param[in] flags Flags for default position calculation.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Searches recursively through the selection tree for dynamic
+ * \ref SEL_EXPRESSION elements that define the \c gmx_ana_selmethod_t::pupdate
+ * function.
+ * For each such element found, position calculation is initialized
+ * for the maximal evaluation group.
+ * The type of the calculation is determined by \p type and \p flags.
+ * No calculation is initialized if \p type equals \ref POS_ATOM and
+ * the method also defines the \c gmx_ana_selmethod_t::update method.
+ */
+static int
+init_item_comg(t_selelem *sel, gmx_ana_poscalc_coll_t *pcc,
+ e_poscalc_t type, int flags)
+{
+ t_selelem *child;
+ int rc;
+
+ /* Initialize COM calculation for dynamic selections now that we know the maximal evaluation group */
+ if (sel->type == SEL_EXPRESSION && sel->u.expr.method
+ && sel->u.expr.method->pupdate)
+ {
+ if (!sel->u.expr.method->update || type != POS_ATOM)
+ {
+ /* Create a default calculation if one does not yet exist */
+ int cflags;
+ cflags = 0;
+ if (!(sel->cdata->flags & SEL_CDATA_STATICEVAL))
+ {
+ cflags |= POS_DYNAMIC;
+ }
+ if (!sel->u.expr.pc)
+ {
+ cflags |= flags;
+ rc = gmx_ana_poscalc_create(&sel->u.expr.pc, pcc, type, cflags);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ else
+ {
+ gmx_ana_poscalc_set_flags(sel->u.expr.pc, cflags);
+ }
+ gmx_ana_poscalc_set_maxindex(sel->u.expr.pc, sel->cdata->gmax);
+ snew(sel->u.expr.pos, 1);
+ gmx_ana_poscalc_init_pos(sel->u.expr.pc, sel->u.expr.pos);
+ }
+ }
+
+ /* Call recursively for all children unless the children have already been processed */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ child = sel->child;
+ while (child)
+ {
+ rc = init_item_comg(child, pcc, type, flags);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ child = child->next;
+ }
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * COMPILER DATA FREEING
+ ********************************************************************/
+
+/*! \brief
+ * Frees the allocated compiler data recursively.
+ *
+ * \param sel Root of the selection subtree to process.
+ *
+ * Frees the data allocated for the compilation process.
+ */
+static void
+free_item_compilerdata(t_selelem *sel)
+{
+ t_selelem *child;
+
+ /* Free compilation data */
+ _gmx_selelem_free_compiler_data(sel);
+
+ /* Call recursively for all children unless the children have already been processed */
+ if (sel->type != SEL_SUBEXPRREF)
+ {
+ child = sel->child;
+ while (child)
+ {
+ free_item_compilerdata(child);
+ child = child->next;
+ }
+ }
+}
+
+
+/********************************************************************
+ * MASS AND CHARGE CALCULATION
+ ********************************************************************/
+
+/*! \brief
+ * Initializes total masses and charges for selections.
+ *
+ * \param[in,out] selections Array of selections to update.
+ * \param[in] top Topology information.
+ */
+static void
+calculate_mass_charge(std::vector<gmx::Selection *> *selections,
+ t_topology *top)
+{
+ int b, i;
+
+ for (size_t g = 0; g < selections->size(); ++g)
+ {
+ gmx_ana_selection_t *sel = &selections->at(g)->_sel;
+ bool bMaskOnly = selections->at(g)->hasFlag(gmx::efDynamicMask);
+
+ sel->g = sel->p.g;
+ snew(sel->orgm, sel->p.nr);
+ snew(sel->orgq, sel->p.nr);
+ for (b = 0; b < sel->p.nr; ++b)
+ {
+ sel->orgq[b] = 0;
+ if (top)
+ {
+ sel->orgm[b] = 0;
+ for (i = sel->p.m.mapb.index[b]; i < sel->p.m.mapb.index[b+1]; ++i)
+ {
+ sel->orgm[b] += top->atoms.atom[sel->g->index[i]].m;
+ sel->orgq[b] += top->atoms.atom[sel->g->index[i]].q;
+ }
+ }
+ else
+ {
+ sel->orgm[b] = 1;
+ }
+ }
+ if (sel->bDynamic && !bMaskOnly)
+ {
+ snew(sel->m, sel->p.nr);
+ snew(sel->q, sel->p.nr);
+ for (b = 0; b < sel->p.nr; ++b)
+ {
+ sel->m[b] = sel->orgm[b];
+ sel->q[b] = sel->orgq[b];
+ }
+ }
+ else
+ {
+ sel->m = sel->orgm;
+ sel->q = sel->orgq;
+ }
+ }
+}
+
+
+/********************************************************************
+ * MAIN COMPILATION FUNCTION
+ ********************************************************************/
+
+/*!
+ * \param[in,out] coll Selection collection to be compiled.
+ * \returns 0 on successful compilation, a non-zero error code on error.
+ *
+ * Before compilation, the selection collection should have been initialized
+ * with gmx_ana_selcollection_parse_*().
+ * The compiled selection collection can be passed to
+ * gmx_ana_selcollection_evaluate() to evaluate the selection for a frame.
+ * If an error occurs, \p sc is cleared.
+ *
+ * The covered fraction information in \p sc is initialized to
+ * \ref CFRAC_NONE.
+ */
+int
+gmx_ana_selcollection_compile(gmx::SelectionCollection *coll)
+{
+ gmx_ana_selcollection_t *sc = &coll->_impl->_sc;
+ gmx_sel_evaluate_t evaldata;
+ t_selelem *item;
+ e_poscalc_t post;
+ size_t i;
+ int flags;
+ int rc;
+ bool bDebug = (coll->_impl->_debugLevel >= 2
+ && coll->_impl->_debugLevel != 3);
+
+ rc = _gmx_sel_mempool_create(&sc->mempool);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ _gmx_sel_evaluate_init(&evaldata, sc->mempool, &sc->gall,
+ sc->top, NULL, NULL);
+
+ /* Clear the symbol table because it is not possible to parse anything
+ * after compilation, and variable references in the symbol table can
+ * also mess up the compilation and/or become invalid.
+ */
+ coll->_impl->clearSymbolTable();
+
+ /* Loop through selections and initialize position keyword defaults if no
+ * other value has been provided.
+ */
+ for (i = 0; i < sc->sel.size(); ++i)
+ {
+ gmx::Selection *sel = sc->sel[i];
+ init_pos_keyword_defaults(sel->_sel.selelem,
+ coll->_impl->_spost.c_str(),
+ coll->_impl->_rpost.c_str(),
+ sel);
+ }
+
+ /* Remove any unused variables. */
+ sc->root = remove_unused_subexpressions(sc->root);
+ /* Extract subexpressions into separate roots */
+ sc->root = extract_subexpressions(sc->root);
+
+ /* Initialize the evaluation callbacks and process the tree structure
+ * to conform to the expectations of the callback functions. */
+ /* Also, initialize and allocate the compiler data structure */
+ item = sc->root;
+ while (item)
+ {
+ /* Process gmx_boolean and arithmetic expressions. */
+ optimize_gmx_boolean_expressions(item);
+ reorder_gmx_boolean_static_children(item);
+ if (!optimize_arithmetic_expressions(item))
+ {
+ /* FIXME: Clean up the collection */
+ return -1;
+ }
+ /* Initialize evaluation function. */
+ if (!init_item_evalfunc(item))
+ {
+ /* FIXME: Clean up the collection */
+ return -1;
+ }
+ setup_memory_pooling(item, sc->mempool);
+ /* Initialize the compiler data */
+ init_item_compilerdata(item);
+ init_item_staticeval(item);
+ item = item->next;
+ }
+ /* Initialize subexpression flags and evaluation output.
+ * Requires compiler flags for the full tree. */
+ item = sc->root;
+ while (item)
+ {
+ init_item_subexpr_flags(item);
+ init_item_evaloutput(item);
+ item = item->next;
+ }
+ /* Initialize minimum/maximum index groups.
+ * Requires evaluation output for the full tree. */
+ item = sc->root;
+ while (item)
+ {
+ init_item_minmax_groups(item);
+ item = item->next;
+ }
+ /* Initialize the evaluation index groups */
+ initialize_evalgrps(sc);
+
+ if (bDebug)
+ {
+ fprintf(stderr, "\nTree after initial compiler processing:\n");
+ coll->printTree(stderr, false);
+ }
+
+ /* Evaluate all static parts of the selection and analyze the tree
+ * to allocate enough memory to store the value of each dynamic subtree. */
+ item = sc->root;
+ while (item)
+ {
+ if (item->child->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
+ {
+ mark_subexpr_dynamic(item->child, TRUE);
+ }
+ set_evaluation_function(item, &analyze_static);
+ rc = item->evaluate(&evaldata, item, NULL);
+ if (rc != 0)
+ {
+ /* FIXME: Clean up the collection */
+ return rc;
+ }
+ item = item->next;
+ }
+
+ /* At this point, static subexpressions no longer have references to them,
+ * so they can be removed. */
+ sc->root = remove_unused_subexpressions(sc->root);
+
+ if (bDebug)
+ {
+ fprintf(stderr, "\nTree after first analysis pass:\n");
+ coll->printTree(stderr, false);
+ }
+
+ /* Do a second pass to evaluate static parts of common subexpressions */
+ item = sc->root;
+ while (item)
+ {
+ if (item->child->cdata->flags & SEL_CDATA_COMMONSUBEXPR)
+ {
+ gmx_bool bMinMax = item->child->cdata->flags & SEL_CDATA_MINMAXALLOC;
+
+ mark_subexpr_dynamic(item->child, FALSE);
+ item->child->u.cgrp.isize = 0;
+ /* We won't clear item->child->v.u.g here, because it may
+ * be static, and hence actually point to item->child->cdata->gmax,
+ * which is used below. We could also check whether this is the
+ * case and only clear the group otherwise, but because the value
+ * is actually overwritten immediately in the evaluate call, we
+ * won't, because similar problems may arise if gmax handling ever
+ * changes and the check were not updated.
+ * For the same reason, we clear the min/max flag so that the
+ * evaluation group doesn't get messed up. */
+ set_evaluation_function(item, &analyze_static);
+ item->child->cdata->flags &= ~SEL_CDATA_MINMAXALLOC;
+ rc = item->evaluate(&evaldata, item->child, item->child->cdata->gmax);
+ if (bMinMax)
+ {
+ item->child->cdata->flags |= SEL_CDATA_MINMAXALLOC;
+ }
+ if (rc != 0)
+ {
+ /* FIXME: Clean up the collection */
+ return rc;
+ }
+ }
+ item = item->next;
+ }
+
+ /* We need a yet another pass of subexpression removal to remove static
+ * subexpressions referred to by common dynamic subexpressions. */
+ sc->root = remove_unused_subexpressions(sc->root);
+
+ if (bDebug)
+ {
+ fprintf(stderr, "\nTree after second analysis pass:\n");
+ coll->printTree(stderr, false);
+ }
+
+ /* Initialize evaluation groups, position calculations for methods, perform
+ * some final optimization, and free the memory allocated for the
+ * compilation. */
+ /* By default, use whole residues/molecules. */
+ flags = POS_COMPLWHOLE;
+ rc = gmx_ana_poscalc_type_from_enum(coll->_impl->_rpost.c_str(), &post, &flags);
+ if (rc != 0)
+ {
+ gmx_bug("invalid default reference position type");
+ /* FIXME: Clean up the collection */
+ return rc;
+ }
+ item = sc->root;
+ while (item)
+ {
+ init_root_item(item, &sc->gall);
+ postprocess_item_subexpressions(item);
+ rc = init_item_comg(item, sc->pcc, post, flags);
+ if (rc != 0)
+ {
+ /* FIXME: Clean up the collection */
+ return rc;
+ }
+ free_item_compilerdata(item);
+ item = item->next;
+ }
+
+ /* Allocate memory for the evaluation memory pool. */
+ rc = _gmx_sel_mempool_reserve(sc->mempool, 0);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ /* Finish up by calculating total masses and charges. */
+ calculate_mass_charge(&sc->sel, sc->top);
+
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in evaluate.h.
+ *
+ * \todo
+ * One of the major bottlenecks for selection performance is that all the
+ * evaluation is carried out for atoms.
+ * There are several cases when the evaluation could be done for residues
+ * or molecules instead, including keywords that select by residue and
+ * cases where residue centers are used as reference positions.
+ * Implementing this would require a mechanism for recognizing whether
+ * something can be evaluated by residue/molecule instead by atom, and
+ * converting selections by residue/molecule into selections by atom
+ * when necessary.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <maths.h>
+#include <smalloc.h>
+#include <vec.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "evaluate.h"
+#include "mempool.h"
+#include "selectioncollection-impl.h"
+#include "selelem.h"
+
+/*!
+ * \param[in] fp File handle to receive the output.
+ * \param[in] evalfunc Function pointer to print.
+ */
+void
+_gmx_sel_print_evalfunc_name(FILE *fp, sel_evalfunc evalfunc)
+{
+ if (!evalfunc)
+ fprintf(fp, "none");
+ else if (evalfunc == &_gmx_sel_evaluate_root)
+ fprintf(fp, "root");
+ else if (evalfunc == &_gmx_sel_evaluate_static)
+ fprintf(fp, "static");
+ else if (evalfunc == &_gmx_sel_evaluate_subexpr_simple)
+ fprintf(fp, "subexpr_simple");
+ else if (evalfunc == &_gmx_sel_evaluate_subexpr_staticeval)
+ fprintf(fp, "subexpr_staticeval");
+ else if (evalfunc == &_gmx_sel_evaluate_subexpr)
+ fprintf(fp, "subexpr");
+ else if (evalfunc == &_gmx_sel_evaluate_subexprref_simple)
+ fprintf(fp, "ref_simple");
+ else if (evalfunc == &_gmx_sel_evaluate_subexprref)
+ fprintf(fp, "ref");
+ else if (evalfunc == &_gmx_sel_evaluate_method)
+ fprintf(fp, "method");
+ else if (evalfunc == &_gmx_sel_evaluate_modifier)
+ fprintf(fp, "mod");
+ else if (evalfunc == &_gmx_sel_evaluate_not)
+ fprintf(fp, "not");
+ else if (evalfunc == &_gmx_sel_evaluate_and)
+ fprintf(fp, "and");
+ else if (evalfunc == &_gmx_sel_evaluate_or)
+ fprintf(fp, "or");
+ else if (evalfunc == &_gmx_sel_evaluate_arithmetic)
+ fprintf(fp, "arithmetic");
+ else
+ fprintf(fp, "%p", (void*)(evalfunc));
+}
+
+/*!
+ * \param[out] data Evaluation data structure to initialize.
+ * \param[in] mp Memory pool for intermediate evaluation values.
+ * \param[in] gall Index group with all the atoms.
+ * \param[in] top Topology structure for evaluation.
+ * \param[in] fr New frame for evaluation.
+ * \param[in] pbc New PBC information for evaluation.
+ */
+void
+_gmx_sel_evaluate_init(gmx_sel_evaluate_t *data,
+ gmx_sel_mempool_t *mp, gmx_ana_index_t *gall,
+ t_topology *top, t_trxframe *fr, t_pbc *pbc)
+{
+ data->mp = mp;
+ data->gall = gall;
+ data->top = top;
+ data->fr = fr;
+ data->pbc = pbc;
+}
+
+/*! \brief
+ * Recursively initializes the flags for evaluation.
+ *
+ * \param[in,out] sel Selection element to clear.
+ *
+ * The \ref SEL_INITFRAME flag is set for \ref SEL_EXPRESSION elements whose
+ * method defines the \p init_frame callback (see sel_framefunc()), and
+ * cleared for other elements.
+ *
+ * The \ref SEL_EVALFRAME flag is cleared for all elements.
+ */
+static void
+init_frame_eval(t_selelem *sel)
+{
+ while (sel)
+ {
+ sel->flags &= ~(SEL_INITFRAME | SEL_EVALFRAME);
+ if (sel->type == SEL_EXPRESSION)
+ {
+ if (sel->u.expr.method && sel->u.expr.method->init_frame)
+ {
+ sel->flags |= SEL_INITFRAME;
+ }
+ }
+ if (sel->child && sel->type != SEL_SUBEXPRREF)
+ {
+ init_frame_eval(sel->child);
+ }
+ sel = sel->next;
+ }
+}
+
+/*!
+ * \param[in,out] sc The selection collection to evaluate.
+ * \param[in] fr Frame for which the evaluation should be carried out.
+ * \param[in] pbc PBC data, or NULL if no PBC should be used.
+ * \returns 0 on successful evaluation, a non-zero error code on error.
+ *
+ * This functions sets the global variables for topology, frame and PBC,
+ * clears some information in the selection to initialize the evaluation
+ * for a new frame, and evaluates \p sel and all the selections pointed by
+ * the \p next pointers of \p sel.
+ *
+ * This is the only function that user code should call if they want to
+ * evaluate a selection for a new frame.
+ */
+int
+gmx_ana_selcollection_evaluate(gmx_ana_selcollection_t *sc,
+ t_trxframe *fr, t_pbc *pbc)
+{
+ gmx_sel_evaluate_t data;
+ t_selelem *sel;
+ int rc;
+
+ _gmx_sel_evaluate_init(&data, sc->mempool, &sc->gall, sc->top, fr, pbc);
+ init_frame_eval(sc->root);
+ sel = sc->root;
+ while (sel)
+ {
+ /* Clear the evaluation group of subexpressions */
+ if (sel->child && sel->child->type == SEL_SUBEXPR)
+ {
+ sel->child->u.cgrp.isize = 0;
+ /* Not strictly necessary, because the value will be overwritten
+ * during first evaluation of the subexpression anyways, but we
+ * clear the group for clarity. Note that this is _not_ done during
+ * compilation because of some additional complexities involved
+ * (see compiler.c), so it should not be relied upon in
+ * _gmx_sel_evaluate_subexpr(). */
+ if (sel->child->v.type == GROUP_VALUE)
+ {
+ sel->child->v.u.g->isize = 0;
+ }
+ }
+ if (sel->evaluate)
+ {
+ rc = sel->evaluate(&data, sel, NULL);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ sel = sel->next;
+ }
+ /* Update selection information */
+ for (size_t g = 0; g < sc->sel.size(); ++g)
+ {
+ gmx_ana_selection_t *sel = &sc->sel[g]->_sel;
+
+ if (sel->m != sel->orgm)
+ {
+ for (int i = 0; i < sel->p.nr; ++i)
+ {
+ sel->m[i] = sel->orgm[sel->p.m.refid[i]];
+ sel->q[i] = sel->orgq[sel->p.m.refid[i]];
+ }
+ }
+ if (sel->bCFracDyn)
+ {
+ sel->cfrac = _gmx_selelem_estimate_coverfrac(sel->selelem);
+ sel->avecfrac += sel->cfrac;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \param[in,out] sc The selection collection to evaluate.
+ * \param[in] nframes Total number of frames.
+ * \returns 0 on successful evaluation, a non-zero error code on error.
+ */
+int
+gmx_ana_selcollection_evaluate_fin(gmx_ana_selcollection_t *sc, int nframes)
+{
+ t_selelem *sel;
+
+ for (size_t g = 0; g < sc->sel.size(); ++g)
+ {
+ gmx_ana_selection_t *sel = &sc->sel[g]->_sel;
+ bool bMaskOnly = sc->sel[g]->hasFlag(gmx::efDynamicMask);
+ t_selelem *elem = sel->selelem;
+ if (sel->bDynamic)
+ {
+ gmx_ana_index_copy(sel->g, elem->v.u.g, FALSE);
+ sel->g->name = NULL;
+ gmx_ana_indexmap_update(&sel->p.m, sel->g, bMaskOnly);
+ sel->p.nr = sel->p.m.nr;
+ }
+
+ if (sel->bCFracDyn)
+ {
+ sel->avecfrac /= nframes;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates each child of \p sel in \p g.
+ */
+int
+_gmx_sel_evaluate_children(gmx_sel_evaluate_t *data, t_selelem *sel,
+ gmx_ana_index_t *g)
+{
+ t_selelem *child;
+ int rc;
+
+ child = sel->child;
+ while (child)
+ {
+ if (child->evaluate)
+ {
+ rc = child->evaluate(data, child, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ child = child->next;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated
+ * (not used, can be NULL).
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates the first child element in the group defined by \p sel->u.cgrp.
+ * If \p sel->u.cgrp is empty, nothing is done.
+ * The value of \p sel is not touched (root elements do not evaluate to
+ * values).
+ *
+ * This function can be used as \c t_selelem::evaluate for \ref SEL_ROOT
+ * elements.
+ */
+int
+_gmx_sel_evaluate_root(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ int rc;
+
+ if (sel->u.cgrp.isize == 0 || !sel->child->evaluate)
+ {
+ return 0;
+ }
+
+ rc = sel->child->evaluate(data, sel->child,
+ sel->u.cgrp.isize < 0 ? NULL : &sel->u.cgrp);
+
+ return rc;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 for success.
+ *
+ * Sets the value of \p sel to the intersection of \p g and \p sel->u.cgrp.
+ *
+ * This function can be used as \c t_selelem::evaluate for \ref SEL_CONST
+ * elements with value type \ref GROUP_VALUE.
+ */
+int
+_gmx_sel_evaluate_static(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ gmx_ana_index_intersection(sel->v.u.g, &sel->u.cgrp, g);
+ return 0;
+}
+
+
+/*********************************************************************
+ * SUBEXPRESSION EVALUATION
+ *********************************************************************/
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates the child element (there should be exactly one) in \p g.
+ * The compiler has taken care that the child actually stores the evaluated
+ * value in the value pointer of this element.
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_SUBEXPR
+ * elements that are used only once, and hence do not need full subexpression
+ * handling.
+ */
+int
+_gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ int rc;
+
+ if (sel->child->evaluate)
+ {
+ rc = sel->child->evaluate(data, sel->child, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ sel->v.nr = sel->child->v.nr;
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * If this is the first call for this frame, evaluates the child element
+ * there should be exactly one in \p g.
+ * The compiler has taken care that the child actually stores the evaluated
+ * value in the value pointer of this element.
+ * Assumes that \p g is persistent for the duration of the whole evaluation.
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_SUBEXPR
+ * elements that have a static evaluation group, and hence do not need full
+ * subexpression handling.
+ */
+int
+_gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ if (sel->u.cgrp.isize == 0)
+ {
+ int rc;
+
+ rc = sel->child->evaluate(data, sel->child, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ sel->v.nr = sel->child->v.nr;
+ gmx_ana_index_set(&sel->u.cgrp, g->isize, g->index, sel->u.cgrp.name, 0);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Finds the part of \p g for which the subexpression
+ * has not yet been evaluated by comparing \p g to \p sel->u.cgrp.
+ * If the part is not empty, the child expression is evaluated for this
+ * part, and the results merged to the old values of the child.
+ * The value of \p sel itself is undefined after the call.
+ *
+ * \todo
+ * The call to gmx_ana_index_difference() can take quite a lot of unnecessary
+ * time if the subexpression is evaluated either several times for the same
+ * group or for completely distinct groups.
+ * However, in the majority of cases, these situations occur when
+ * _gmx_sel_evaluate_subexpr_staticeval() can be used, so this should not be a
+ * major problem.
+ */
+int
+_gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ gmx_ana_index_t gmiss;
+ int rc;
+
+ if (sel->u.cgrp.isize == 0)
+ {
+ char *name;
+ void *old_ptr = sel->child->v.u.ptr;
+ int old_nalloc = sel->child->v.nalloc;
+ _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
+ rc = sel->child->evaluate(data, sel->child, g);
+ _gmx_selvalue_setstore_alloc(&sel->child->v, old_ptr, old_nalloc);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ /* We need to keep the name for the cgrp across the copy to avoid
+ * problems if g has a name set. */
+ name = sel->u.cgrp.name;
+ gmx_ana_index_copy(&sel->u.cgrp, g, FALSE);
+ sel->u.cgrp.name = name;
+ gmiss.isize = 0;
+ }
+ else
+ {
+ /* We allocate some extra memory here to avoid some computation. */
+ rc = _gmx_sel_mempool_alloc_group(data->mp, &gmiss, g->isize);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_index_difference(&gmiss, g, &sel->u.cgrp);
+ if (gmiss.isize == 0)
+ {
+ _gmx_sel_mempool_free_group(data->mp, &gmiss);
+ }
+ }
+ if (gmiss.isize > 0)
+ {
+ rc = _gmx_selelem_mempool_reserve(sel->child, gmiss.isize);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ /* Evaluate the missing values for the child */
+ rc = sel->child->evaluate(data, sel->child, &gmiss);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ /* Merge the missing values to the existing ones. */
+ if (sel->v.type == GROUP_VALUE)
+ {
+ gmx_ana_index_merge(sel->v.u.g, sel->child->v.u.g, sel->v.u.g);
+ }
+ else
+ {
+ int i, j, k;
+
+ i = sel->u.cgrp.isize - 1;
+ j = gmiss.isize - 1;
+ /* TODO: This switch is kind of ugly, but it may be difficult to
+ * do this portably without C++ templates. */
+ switch (sel->v.type)
+ {
+ case INT_VALUE:
+ for (k = sel->u.cgrp.isize + gmiss.isize - 1; k >= 0; k--)
+ {
+ if (i < 0 || (j >= 0 && sel->u.cgrp.index[i] < gmiss.index[j]))
+ {
+ sel->v.u.i[k] = sel->v.u.i[j--];
+ }
+ else
+ {
+ sel->v.u.i[k] = sel->child->v.u.i[i--];
+ }
+ }
+ break;
+
+ case REAL_VALUE:
+ for (k = sel->u.cgrp.isize + gmiss.isize - 1; k >= 0; k--)
+ {
+ if (i < 0 || (j >= 0 && sel->u.cgrp.index[i] < gmiss.index[j]))
+ {
+ sel->v.u.r[k] = sel->v.u.r[j--];
+ }
+ else
+ {
+ sel->v.u.r[k] = sel->child->v.u.r[i--];
+ }
+ }
+ break;
+
+ case STR_VALUE:
+ for (k = sel->u.cgrp.isize + gmiss.isize - 1; k >= 0; k--)
+ {
+ if (i < 0 || (j >= 0 && sel->u.cgrp.index[i] < gmiss.index[j]))
+ {
+ sel->v.u.s[k] = sel->v.u.s[j--];
+ }
+ else
+ {
+ sel->v.u.s[k] = sel->child->v.u.s[i--];
+ }
+ }
+ break;
+
+ case POS_VALUE:
+ /* TODO: Implement this */
+ gmx_impl("position subexpressions not implemented properly");
+ return -1;
+
+ case NO_VALUE:
+ case GROUP_VALUE:
+ gmx_bug("internal error");
+ return -1;
+ }
+ }
+ gmx_ana_index_merge(&sel->u.cgrp, &sel->u.cgrp, &gmiss);
+ _gmx_selelem_mempool_release(sel->child);
+ _gmx_sel_mempool_free_group(data->mp, &gmiss);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 for success.
+ *
+ * Sets the value pointers of the child and its child to point to the same
+ * memory as the value pointer of this element to avoid copying, and then
+ * evaluates evaluates the child.
+ *
+ * This function is used as \c t_selelem:evaluate for \ref SEL_SUBEXPRREF
+ * elements for which the \ref SEL_SUBEXPR does not have other references.
+ */
+int
+_gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ if (g)
+ {
+ int rc;
+
+ _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
+ _gmx_selvalue_setstore_alloc(&sel->child->child->v, sel->v.u.ptr,
+ sel->child->child->v.nalloc);
+ rc = sel->child->evaluate(data, sel->child, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ sel->v.nr = sel->child->v.nr;
+ if (sel->u.param)
+ {
+ sel->u.param->val.nr = sel->v.nr;
+ if (sel->u.param->nvalptr)
+ {
+ *sel->u.param->nvalptr = sel->u.param->val.nr;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * If the value type is \ref POS_VALUE, the value of the child is simply
+ * copied to set the value of \p sel (the child subexpression should
+ * already have been evaluated by its root).
+ * If the value type is something else, the child is evaluated for the
+ * group \p g, and the value of the child is then copied.
+ * There should be only one child element.
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_SUBEXPRREF
+ * elements.
+ */
+int
+_gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ t_selelem *expr;
+ int i, j;
+
+ if (g)
+ {
+ int rc;
+
+ rc = sel->child->evaluate(data, sel->child, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ expr = sel->child;
+ switch (sel->v.type)
+ {
+ case INT_VALUE:
+ if (!g)
+ {
+ sel->v.nr = expr->v.nr;
+ memcpy(sel->v.u.i, expr->v.u.i, sel->v.nr*sizeof(*sel->v.u.i));
+ }
+ else
+ {
+ sel->v.nr = g->isize;
+ /* Extract the values corresponding to g */
+ for (i = j = 0; i < g->isize; ++i, ++j)
+ {
+ while (sel->child->u.cgrp.index[j] < g->index[i])
+ {
+ ++j;
+ }
+ sel->v.u.i[i] = expr->v.u.i[j];
+ }
+ }
+ break;
+
+ case REAL_VALUE:
+ if (!g)
+ {
+ sel->v.nr = expr->v.nr;
+ memcpy(sel->v.u.r, expr->v.u.r, sel->v.nr*sizeof(*sel->v.u.r));
+ }
+ else
+ {
+ sel->v.nr = g->isize;
+ /* Extract the values corresponding to g */
+ for (i = j = 0; i < g->isize; ++i, ++j)
+ {
+ while (sel->child->u.cgrp.index[j] < g->index[i])
+ {
+ ++j;
+ }
+ sel->v.u.r[i] = expr->v.u.r[j];
+ }
+ }
+ break;
+
+ case STR_VALUE:
+ if (!g)
+ {
+ sel->v.nr = expr->v.nr;
+ memcpy(sel->v.u.s, expr->v.u.s, sel->v.nr*sizeof(*sel->v.u.s));
+ }
+ else
+ {
+ sel->v.nr = g->isize;
+ /* Extract the values corresponding to g */
+ for (i = j = 0; i < g->isize; ++i, ++j)
+ {
+ while (sel->child->u.cgrp.index[j] < g->index[i])
+ {
+ ++j;
+ }
+ sel->v.u.s[i] = expr->v.u.s[j];
+ }
+ }
+ break;
+
+ case POS_VALUE:
+ /* Currently, there is no need to do anything fancy here,
+ * but some future extensions may need a more flexible
+ * implementation. */
+ gmx_ana_pos_copy(sel->v.u.p, expr->v.u.p, FALSE);
+ break;
+
+ case GROUP_VALUE:
+ if (!g)
+ {
+ gmx_ana_index_copy(sel->v.u.g, expr->v.u.g, FALSE);
+ }
+ else
+ {
+ gmx_ana_index_intersection(sel->v.u.g, expr->v.u.g, g);
+ }
+ break;
+
+ default: /* should not be reached */
+ gmx_bug("invalid subexpression reference type");
+ return -1;
+ }
+ /* Store the number of values if needed */
+ if (sel->u.param)
+ {
+ sel->u.param->val.nr = sel->v.nr;
+ if (sel->u.param->nvalptr)
+ {
+ *sel->u.param->nvalptr = sel->u.param->val.nr;
+ }
+ }
+ return 0;
+}
+
+/********************************************************************
+ * METHOD EXPRESSION EVALUATION
+ ********************************************************************/
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates each child of a \ref SEL_EXPRESSION element.
+ * The value of \p sel is not touched.
+ *
+ * This function is not used as \c t_selelem::evaluate,
+ * but is used internally.
+ */
+int
+_gmx_sel_evaluate_method_params(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ t_selelem *child;
+ int rc;
+
+ child = sel->child;
+ while (child)
+ {
+ if (child->evaluate && !(child->flags & SEL_EVALFRAME))
+ {
+ if (child->flags & SEL_ATOMVAL)
+ {
+ rc = child->evaluate(data, child, g);
+ }
+ else
+ {
+ rc = child->evaluate(data, child, NULL);
+ child->flags |= SEL_EVALFRAME;
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ child = child->next;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates all child selections (using _gmx_sel_evaluate_method_params())
+ * to evaluate any parameter values.
+ * If this is the first time this expression is evaluated for
+ * the frame, sel_framefunc() callback is called if one is provided.
+ * If a reference position calculation has been initialized for this element,
+ * the positions are also updated, and sel_updatefunc_pos() is used to
+ * evaluate the value. Otherwise, sel_updatefunc() is used.
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_EXPRESSION
+ * elements.
+ */
+int
+_gmx_sel_evaluate_method(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ int rc;
+
+ rc = _gmx_sel_evaluate_method_params(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if (sel->flags & SEL_INITFRAME)
+ {
+ rc = sel->u.expr.method->init_frame(data->top, data->fr, data->pbc,
+ sel->u.expr.mdata);
+ sel->flags &= ~SEL_INITFRAME;
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (sel->u.expr.pc)
+ {
+ gmx_ana_poscalc_update(sel->u.expr.pc, sel->u.expr.pos, g,
+ data->fr, data->pbc);
+ rc = sel->u.expr.method->pupdate(data->top, data->fr, data->pbc,
+ sel->u.expr.pos, &sel->v,
+ sel->u.expr.mdata);
+ }
+ else
+ {
+ rc = sel->u.expr.method->update(data->top, data->fr, data->pbc, g,
+ &sel->v, sel->u.expr.mdata);
+ }
+ return rc;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates all child selections (using _gmx_sel_evaluate_method_params())
+ * to evaluate any parameter values.
+ * If this is the first time this expression is evaluated for
+ * the frame, sel_framefunc() callback is called if one is provided.
+ * The modifier is then evaluated using sel_updatefunc_pos().
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_MODIFIER
+ * elements.
+ */
+int
+_gmx_sel_evaluate_modifier(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ int rc;
+
+ rc = _gmx_sel_evaluate_method_params(data, sel, g);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if (sel->flags & SEL_INITFRAME)
+ {
+ rc = sel->u.expr.method->init_frame(data->top, data->fr, data->pbc,
+ sel->u.expr.mdata);
+ sel->flags &= ~SEL_INITFRAME;
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (sel->child->v.type != POS_VALUE)
+ {
+ gmx_bug("non-position valued modifiers not implemented");
+ return -1;
+ }
+ rc = sel->u.expr.method->pupdate(data->top, data->fr, data->pbc,
+ sel->child->v.u.p,
+ &sel->v, sel->u.expr.mdata);
+ return rc;
+}
+
+
+/********************************************************************
+ * BOOLEAN EXPRESSION EVALUATION
+ ********************************************************************/
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Evaluates the child element (there should be only one) in the group
+ * \p g, and then sets the value of \p sel to the complement of the
+ * child value.
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_BOOLEAN
+ * elements with \ref BOOL_NOT.
+ */
+int
+_gmx_sel_evaluate_not(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ int rc;
+
+ rc = _gmx_selelem_mempool_reserve(sel->child, g->isize);
+ if (rc == 0)
+ {
+ rc = sel->child->evaluate(data, sel->child, g);
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_index_difference(sel->v.u.g, g, sel->child->v.u.g);
+ _gmx_selelem_mempool_release(sel->child);
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Short-circuiting evaluation of logical AND expressions.
+ *
+ * Starts by evaluating the first child element in the group \p g.
+ * The each following child element is evaluated in the intersection
+ * of all the previous values until all children have been evaluated
+ * or the intersection becomes empty.
+ * The value of \p sel is set to the intersection of all the (evaluated)
+ * child values.
+ *
+ * If the first child does not have an evaluation function, it is skipped
+ * and the evaluation is started at the second child.
+ * This happens if the first child is a constant expression and during
+ * compilation it was detected that the evaluation group is always a subset
+ * of the constant group
+ * (currently, the compiler never detects this).
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_BOOLEAN
+ * elements with \ref BOOL_AND.
+ */
+int
+_gmx_sel_evaluate_and(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ t_selelem *child;
+ int rc;
+
+ child = sel->child;
+ /* Skip the first child if it does not have an evaluation function. */
+ if (!child->evaluate)
+ {
+ child = child->next;
+ }
+ rc = _gmx_selelem_mempool_reserve(child, g->isize);
+ if (rc == 0)
+ {
+ rc = child->evaluate(data, child, g);
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_index_copy(sel->v.u.g, child->v.u.g, FALSE);
+ _gmx_selelem_mempool_release(child);
+ child = child->next;
+ while (child && sel->v.u.g->isize > 0)
+ {
+ rc = _gmx_selelem_mempool_reserve(child, sel->v.u.g->isize);
+ if (rc == 0)
+ {
+ rc = child->evaluate(data, child, sel->v.u.g);
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_index_intersection(sel->v.u.g, sel->v.u.g, child->v.u.g);
+ _gmx_selelem_mempool_release(child);
+ child = child->next;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Short-circuiting evaluation of logical OR expressions.
+ *
+ * Starts by evaluating the first child element in the group \p g.
+ * For each subsequent child, finds the part of \p g that is not
+ * included the value of any previous child, and evaluates the child
+ * in that group until the last child is evaluated or all of \p g
+ * is included in some child value.
+ * The value of \p sel is set to the union of all the (evaluated)
+ * child values.
+ *
+ * If the first child does not have an evaluation function, its value is
+ * used without evaluation.
+ * This happens if the first child is a constant expression, the selection
+ * has been compiled, and the evaluation group is the same for each frame.
+ * In this case, the compiler has taken care of that the child value is a
+ * subset of \p g, making it unnecessary to evaluate it.
+ *
+ * This function is used as \c t_selelem::evaluate for \ref SEL_BOOLEAN
+ * elements with \ref BOOL_OR.
+ */
+int
+_gmx_sel_evaluate_or(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g)
+{
+ t_selelem *child;
+ gmx_ana_index_t tmp, tmp2;
+ int rc;
+
+ child = sel->child;
+ if (child->evaluate)
+ {
+ rc = _gmx_selelem_mempool_reserve(child, g->isize);
+ if (rc == 0)
+ {
+ rc = child->evaluate(data, child, g);
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_index_partition(sel->v.u.g, &tmp, g, child->v.u.g);
+ _gmx_selelem_mempool_release(child);
+ }
+ else
+ {
+ gmx_ana_index_partition(sel->v.u.g, &tmp, g, child->v.u.g);
+ }
+ child = child->next;
+ while (child && tmp.isize > 0)
+ {
+ tmp.name = NULL;
+ rc = _gmx_selelem_mempool_reserve(child, tmp.isize);
+ if (rc == 0)
+ {
+ rc = child->evaluate(data, child, &tmp);
+ }
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_index_partition(&tmp, &tmp2, &tmp, child->v.u.g);
+ _gmx_selelem_mempool_release(child);
+ sel->v.u.g->isize += tmp.isize;
+ tmp.isize = tmp2.isize;
+ tmp.index = tmp2.index;
+ child = child->next;
+ }
+ gmx_ana_index_sort(sel->v.u.g);
+ return 0;
+}
+
+
+/********************************************************************
+ * ARITHMETIC EVALUATION
+ ********************************************************************/
+
+/*!
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g Group for which \p sel should be evaluated.
+ * \returns 0 on success, a non-zero error code on error.
+ */
+int
+_gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t *data, t_selelem *sel,
+ gmx_ana_index_t *g)
+{
+ t_selelem *left, *right;
+ int n, i, i1, i2;
+ real lval, rval=0., val=0.;
+ int rc;
+
+ left = sel->child;
+ right = left->next;
+
+ if (left->mempool)
+ {
+ _gmx_selvalue_setstore(&left->v, sel->v.u.ptr);
+ if (right)
+ {
+ rc = _gmx_selelem_mempool_reserve(right, g->isize);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ }
+ else if (right && right->mempool)
+ {
+ _gmx_selvalue_setstore(&right->v, sel->v.u.ptr);
+ }
+ rc = _gmx_sel_evaluate_children(data, sel, g);
+
+ n = (sel->flags & SEL_SINGLEVAL) ? 1 : g->isize;
+ sel->v.nr = n;
+ for (i = i1 = i2 = 0; i < n; ++i)
+ {
+ lval = left->v.u.r[i1];
+ if (sel->u.arith.type != ARITH_NEG)
+ {
+ rval = right->v.u.r[i2];
+ }
+ switch (sel->u.arith.type)
+ {
+ case ARITH_PLUS: val = lval + rval; break;
+ case ARITH_MINUS: val = lval - rval; break;
+ case ARITH_NEG: val = -lval; break;
+ case ARITH_MULT: val = lval * rval; break;
+ case ARITH_DIV: val = lval / rval; break;
+ case ARITH_EXP: val = pow(lval, rval); break;
+ }
+ sel->v.u.r[i] = val;
+ if (!(left->flags & SEL_SINGLEVAL))
+ {
+ ++i1;
+ }
+ if (sel->u.arith.type != ARITH_NEG && !(right->flags & SEL_SINGLEVAL))
+ {
+ ++i2;
+ }
+ }
+
+ if (left->mempool)
+ {
+ _gmx_selvalue_setstore(&left->v, NULL);
+ if (right)
+ {
+ _gmx_selelem_mempool_release(right);
+ }
+ }
+ else if (right && right->mempool)
+ {
+ _gmx_selvalue_setstore(&right->v, NULL);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Evaluation functions for sel_evalfunc().
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ * Users should only use SelectionCollection::evaluate() to evaluate
+ * selections.
+ *
+ * The functions defined in this header file are all the possible values
+ * for the \c t_selelem::evaluate field (in addition to NULL).
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_EVALUATE_H
+#define GMX_SELECTION_EVALUATE_H
+
+#include <typedefs.h>
+
+#include "gromacs/selection/indexutil.h"
+
+#include "selelem.h"
+
+struct gmx_sel_mempool_t;
+
+/*! \internal \brief
+ * Data structure for passing information required during evaluation.
+ */
+typedef struct gmx_sel_evaluate_t
+{
+ /** Memory pool for intermediate values. */
+ struct gmx_sel_mempool_t *mp;
+ /** Index group that contains all the atoms. */
+ gmx_ana_index_t *gall;
+ /** Topology information. */
+ t_topology *top;
+ /** Current frame. */
+ t_trxframe *fr;
+ /** PBC data. */
+ t_pbc *pbc;
+} gmx_sel_evaluate_t;
+
+/*! \name Utility functions
+ */
+/*@{*/
+/** Initializes an evaluation data structure. */
+void
+_gmx_sel_evaluate_init(gmx_sel_evaluate_t *data,
+ struct gmx_sel_mempool_t *mp, gmx_ana_index_t *gall,
+ t_topology *top, t_trxframe *fr, t_pbc *pbc);
+/** Evaluates the children of a general selection element. */
+int
+_gmx_sel_evaluate_children(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates the children of a \ref SEL_EXPRESSION element. */
+int
+_gmx_sel_evaluate_method_params(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/*@}*/
+
+/*! \name Misc. evaluation functions
+ */
+/*@{*/
+/** Evaluates a root selection element. */
+int
+_gmx_sel_evaluate_root(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a static group selection element. */
+int
+_gmx_sel_evaluate_static(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates an arithmetic expression element. */
+int
+_gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/*@}*/
+
+/*! \name Subexpression evaluation functions
+ */
+/*@{*/
+/** Evaluates a subexpression when there is only one reference. */
+int
+_gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a subexpression when the evaluation group is static. */
+int
+_gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a subexpression. */
+int
+_gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a subexpression reference when there are no other references. */
+int
+_gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a subexpression reference. */
+int
+_gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/*@}*/
+
+/*! \name Method evaluation functions
+ */
+/*@{*/
+
+/** Evaluates a method expression. */
+int
+_gmx_sel_evaluate_method(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a modifier expression. */
+int
+_gmx_sel_evaluate_modifier(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/*@}*/
+
+/*! \name Boolean evaluation functions
+ */
+/*@{*/
+/** Evaluates a boolean NOT element. */
+int
+_gmx_sel_evaluate_not(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a boolean AND element with short-circuiting. */
+int
+_gmx_sel_evaluate_and(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/** Evaluates a boolean OR element with short-circuiting. */
+int
+_gmx_sel_evaluate_or(gmx_sel_evaluate_t *data, t_selelem *sel, gmx_ana_index_t *g);
+/*@}*/
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in indexutil.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <index.h>
+#include <smalloc.h>
+#include <string2.h>
+#include <typedefs.h>
+#include <gmx_fatal.h>
+
+#include "gromacs/selection/indexutil.h"
+
+/********************************************************************
+ * gmx_ana_indexgrps_t functions
+ ********************************************************************/
+
+/*! \internal \brief
+ * Stores a set of index groups.
+ */
+struct gmx_ana_indexgrps_t
+{
+ /** Number of index groups. */
+ int nr;
+ /** Array of index groups. */
+ gmx_ana_index_t *g;
+};
+
+/*!
+ * \param[out] g Index group structure.
+ * \param[in] ngrps Number of groups for which memory is allocated.
+ */
+void
+gmx_ana_indexgrps_alloc(gmx_ana_indexgrps_t **g, int ngrps)
+{
+ snew(*g, 1);
+ (*g)->nr = ngrps;
+ snew((*g)->g, ngrps);
+}
+
+/*!
+ * \param[out] g Index group structure.
+ * \param[in] ngrps Number of index groups.
+ * \param[in] isize Array of index group sizes.
+ * \param[in] index Array of pointers to indices of each group.
+ * \param[in] name Array of names of the groups.
+ * \param[in] bFree If TRUE, the \p isize, \p index and \p name arrays
+ * are freed after they have been copied.
+ */
+void
+gmx_ana_indexgrps_set(gmx_ana_indexgrps_t **g, int ngrps, int *isize,
+ atom_id **index, char **name, gmx_bool bFree)
+{
+ int i;
+
+ gmx_ana_indexgrps_alloc(g, ngrps);
+ for (i = 0; i < ngrps; ++i)
+ {
+ gmx_ana_index_set(&(*g)->g[i], isize[i], index[i], name[i], isize[i]);
+ }
+ if (bFree)
+ {
+ sfree(isize);
+ sfree(index);
+ sfree(name);
+ }
+}
+
+/*!
+ * \param[out] g Index group structure.
+ * \param[in] top Topology structure.
+ * \param[in] fnm File name for the index file.
+ * Memory is automatically allocated.
+ *
+ * One or both of \p top or \p fnm can be NULL.
+ * If \p top is NULL, an index file is required and the groups are read
+ * from the file (uses Gromacs routine init_index()).
+ * If \p fnm is NULL, default groups are constructed based on the
+ * topology (uses Gromacs routine analyse()).
+ * If both are null, the index group structure is initialized empty.
+ */
+void
+gmx_ana_indexgrps_init(gmx_ana_indexgrps_t **g, t_topology *top,
+ const char *fnm)
+{
+ t_blocka *block = NULL;
+ char **names = NULL;
+ int i, j;
+
+ if (fnm)
+ {
+ block = init_index(fnm, &names);
+ }
+ else if (top)
+ {
+ block = new_blocka();
+ analyse(&top->atoms, block, &names, FALSE, FALSE);
+ }
+ else
+ {
+ snew(*g, 1);
+ (*g)->nr = 0;
+ (*g)->g = NULL;
+ return;
+ }
+
+ gmx_ana_indexgrps_alloc(g, block->nr);
+ for (i = 0; i < block->nr; ++i)
+ {
+ gmx_ana_index_t *grp = &(*g)->g[i];
+
+ grp->isize = block->index[i+1] - block->index[i];
+ snew(grp->index, grp->isize);
+ for (j = 0; j < grp->isize; ++j)
+ {
+ grp->index[j] = block->a[block->index[i]+j];
+ }
+ grp->name = names[i];
+ grp->nalloc_index = grp->isize;
+ }
+
+ done_blocka(block);
+ sfree(block);
+ sfree(names);
+}
+
+/*!
+ * \param[out] g Index group structure.
+ * \param[in] top Topology structure.
+ * \param[in] fnm File name for the index file.
+ * \param[in] ngrps Number of required groups.
+ * Memory is automatically allocated.
+ *
+ * One of \p top or \p fnm can be NULL, but not both.
+ * If \p top is NULL, an index file is required and the groups are read
+ * from the file (uses Gromacs routine rd_index()).
+ * If \p fnm is NULL, default groups are constructed based on the
+ * topology (uses Gromacs routine get_index()).
+ */
+void
+gmx_ana_indexgrps_get(gmx_ana_indexgrps_t **g, t_topology *top,
+ const char *fnm, int ngrps)
+{
+ int *isize;
+ atom_id **index;
+ char **name;
+
+ snew(isize, ngrps);
+ snew(index, ngrps);
+ snew(name, ngrps);
+ if (!top)
+ {
+ rd_index(fnm, ngrps, isize, index, name);
+ }
+ else
+ {
+ get_index(&(top->atoms), fnm, ngrps, isize, index, name);
+ }
+ gmx_ana_indexgrps_set(g, ngrps, isize, index, name, TRUE);
+}
+
+/*!
+ * \param[out] g Index group structure.
+ * \param[in] fnm File name for the index file.
+ * \param[in] ngrps Number of required groups.
+ * Memory is automatically allocated.
+ *
+ * This is a convenience function for calling the Gromacs routine
+ * rd_index().
+ */
+void
+gmx_ana_indexgrps_rd(gmx_ana_indexgrps_t **g, const char *fnm, int ngrps)
+{
+ gmx_ana_indexgrps_get(g, NULL, fnm, ngrps);
+}
+
+/*!
+ * \param[in] g Index groups structure.
+ *
+ * The pointer \p g is invalid after the call.
+ */
+void
+gmx_ana_indexgrps_free(gmx_ana_indexgrps_t *g)
+{
+ int i;
+
+ if (g->nr == 0)
+ {
+ sfree(g);
+ return;
+ }
+ for (i = 0; i < g->nr; ++i)
+ {
+ gmx_ana_index_deinit(&g->g[i]);
+ }
+ sfree(g->g);
+ g->nr = 0;
+ g->g = NULL;
+ sfree(g);
+}
+
+/*!
+ * \param[out] dest Destination index groups.
+ * \param[in] src Source index groups.
+ *
+ * A deep copy is made for all fields, including the group names.
+ */
+void
+gmx_ana_indexgrps_clone(gmx_ana_indexgrps_t **dest, gmx_ana_indexgrps_t *src)
+{
+ int g;
+
+ gmx_ana_indexgrps_alloc(dest, src->nr);
+ for (g = 0; g < src->nr; ++g)
+ {
+ gmx_ana_index_copy(&(*dest)->g[g], &src->g[g], TRUE);
+ }
+}
+
+/*!
+ * \param[out] g Index group structure.
+ * \returns TRUE if \p g is empty, i.e., has 0 index groups.
+ */
+gmx_bool
+gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t *g)
+{
+ return g->nr == 0;
+}
+
+/*!
+ * \param[in] g Index groups structure.
+ * \param[in] n Index group number to get.
+ * \returns Pointer to the \p n'th index group in \p g.
+ *
+ * The returned pointer should not be freed.
+ */
+gmx_ana_index_t *
+gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t *g, int n)
+{
+ if (n < 0 || n >= g->nr)
+ {
+ return NULL;
+ }
+ return &g->g[n];
+}
+
+/*!
+ * \param[out] dest Output structure.
+ * \param[in] src Input index groups.
+ * \param[in] n Number of the group to extract.
+ * \returns TRUE if \p n is a valid group in \p src, FALSE otherwise.
+ */
+gmx_bool
+gmx_ana_indexgrps_extract(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, int n)
+{
+ if (n < 0 || n >= src->nr)
+ {
+ dest->isize = 0;
+ return FALSE;
+ }
+
+ gmx_ana_index_copy(dest, &src->g[n], TRUE);
+ return TRUE;
+}
+
+/*!
+ * \param[out] dest Output structure.
+ * \param[in] src Input index groups.
+ * \param[in] name Name (or part of the name) of the group to extract.
+ * \returns TRUE if \p name is a valid group in \p src, FALSE otherwise.
+ *
+ * Uses the Gromacs routine find_group() to find the actual group;
+ * the comparison is case-insensitive.
+ */
+gmx_bool
+gmx_ana_indexgrps_find(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, char *name)
+{
+ int i;
+ char **names;
+
+ snew(names, src->nr);
+ for (i = 0; i < src->nr; ++i)
+ {
+ names[i] = src->g[i].name;
+ }
+ i = find_group(name, src->nr, names);
+ sfree(names);
+ if (i == NOTSET)
+ {
+ dest->isize = 0;
+ return FALSE;
+ }
+
+ return gmx_ana_indexgrps_extract(dest, src, i);
+}
+
+/*!
+ * \param[in] g Index groups to print.
+ * \param[in] maxn Maximum number of indices to print
+ * (-1 = print all, 0 = print only names).
+ */
+void
+gmx_ana_indexgrps_print(gmx_ana_indexgrps_t *g, int maxn)
+{
+ int i;
+
+ for (i = 0; i < g->nr; ++i)
+ {
+ fprintf(stderr, " %2d: ", i);
+ gmx_ana_index_dump(&g->g[i], i, maxn);
+ }
+}
+
+/********************************************************************
+ * gmx_ana_index_t functions
+ ********************************************************************/
+
+/*!
+ * \param[in,out] g Index group structure.
+ * \param[in] isize Maximum number of atoms to reserve space for.
+ */
+void
+gmx_ana_index_reserve(gmx_ana_index_t *g, int isize)
+{
+ if (g->nalloc_index < isize)
+ {
+ srenew(g->index, isize);
+ g->nalloc_index = isize;
+ }
+}
+
+/*!
+ * \param[in,out] g Index group structure.
+ *
+ * Resizes the memory allocated for holding the indices such that the
+ * current contents fit.
+ */
+void
+gmx_ana_index_squeeze(gmx_ana_index_t *g)
+{
+ srenew(g->index, g->isize);
+ g->nalloc_index = g->isize;
+}
+
+/*!
+ * \param[out] g Output structure.
+ *
+ * Any contents of \p g are discarded without freeing.
+ */
+void
+gmx_ana_index_clear(gmx_ana_index_t *g)
+{
+ g->isize = 0;
+ g->index = NULL;
+ g->name = NULL;
+ g->nalloc_index = 0;
+}
+
+/*!
+ * \param[out] g Output structure.
+ * \param[in] isize Number of atoms in the new group.
+ * \param[in] index Array of \p isize atoms (can be NULL if \p isize is 0).
+ * \param[in] name Name for the new group (can be NULL).
+ * \param[in] nalloc Number of elements allocated for \p index
+ * (if 0, \p index is not freed in gmx_ana_index_deinit())
+ *
+ * No copy if \p index is made.
+ */
+void
+gmx_ana_index_set(gmx_ana_index_t *g, int isize, atom_id *index, char *name,
+ int nalloc)
+{
+ g->isize = isize;
+ g->index = index;
+ g->name = name;
+ g->nalloc_index = nalloc;
+}
+
+/*!
+ * \param[out] g Output structure.
+ * \param[in] natoms Number of atoms.
+ * \param[in] name Name for the new group (can be NULL).
+ */
+void
+gmx_ana_index_init_simple(gmx_ana_index_t *g, int natoms, char *name)
+{
+ int i;
+
+ g->isize = natoms;
+ snew(g->index, natoms);
+ for (i = 0; i < natoms; ++i)
+ {
+ g->index[i] = i;
+ }
+ g->name = name;
+ g->nalloc_index = natoms;
+}
+
+/*!
+ * \param[in] g Index group structure.
+ *
+ * The pointer \p g is not freed.
+ */
+void
+gmx_ana_index_deinit(gmx_ana_index_t *g)
+{
+ if (g->nalloc_index > 0)
+ {
+ sfree(g->index);
+ }
+ sfree(g->name);
+ gmx_ana_index_clear(g);
+}
+
+/*!
+ * \param[out] dest Destination index group.
+ * \param[in] src Source index group.
+ * \param[in] bAlloc If TRUE, memory is allocated at \p dest; otherwise,
+ * it is assumed that enough memory has been allocated for index.
+ *
+ * A deep copy of the name is only made if \p bAlloc is TRUE.
+ */
+void
+gmx_ana_index_copy(gmx_ana_index_t *dest, gmx_ana_index_t *src, gmx_bool bAlloc)
+{
+ dest->isize = src->isize;
+ if (dest->isize > 0)
+ {
+ if (bAlloc)
+ {
+ snew(dest->index, dest->isize);
+ dest->nalloc_index = dest->isize;
+ }
+ memcpy(dest->index, src->index, dest->isize*sizeof(*dest->index));
+ }
+ if (bAlloc && src->name)
+ {
+ dest->name = strdup(src->name);
+ }
+ else if (bAlloc || src->name)
+ {
+ dest->name = src->name;
+ }
+}
+
+/*!
+ * \param[in] g Index group to print.
+ * \param[in] i Group number to use if the name is NULL.
+ * \param[in] maxn Maximum number of indices to print (-1 = print all).
+ */
+void
+gmx_ana_index_dump(gmx_ana_index_t *g, int i, int maxn)
+{
+ int j, n;
+
+ if (g->name)
+ {
+ fprintf(stderr, "\"%s\"", g->name);
+ }
+ else
+ {
+ fprintf(stderr, "Group %d", i+1);
+ }
+ fprintf(stderr, " (%d atoms)", g->isize);
+ if (maxn != 0)
+ {
+ fprintf(stderr, ":");
+ n = g->isize;
+ if (maxn >= 0 && n > maxn)
+ {
+ n = maxn;
+ }
+ for (j = 0; j < n; ++j)
+ {
+ fprintf(stderr, " %d", g->index[j]+1);
+ }
+ if (n < g->isize)
+ {
+ fprintf(stderr, " ...");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+/*!
+ * \param[in] g Input index group.
+ * \param[in] natoms Number of atoms to check against.
+ *
+ * If any atom index in the index group is less than zero or >= \p natoms,
+ * gmx_fatal() is called.
+ */
+void
+gmx_ana_index_check(gmx_ana_index_t *g, int natoms)
+{
+ int j;
+
+ for (j = 0; j < g->isize; ++j)
+ {
+ if (g->index[j] >= natoms)
+ {
+ gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) "
+ "larger than number of atoms in trajectory (%d atoms)",
+ g->index[j], g->name, g->isize, natoms);
+ }
+ else if (g->index[j] < 0)
+ {
+ gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) "
+ "is less than zero",
+ g->index[j], g->name, g->isize);
+ }
+ }
+}
+
+/*!
+ * \param[in] g Index group to check.
+ * \returns TRUE if the index group is sorted and has no duplicates,
+ * FALSE otherwise.
+ */
+gmx_bool
+gmx_ana_index_check_sorted(gmx_ana_index_t *g)
+{
+ int i;
+
+ for (i = 0; i < g->isize-1; ++i)
+ {
+ if (g->index[i+1] <= g->index[i])
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/********************************************************************
+ * Set operations
+ ********************************************************************/
+
+/** Helper function for gmx_ana_index_sort(). */
+static int
+cmp_atomid(const void *a, const void *b)
+{
+ if (*(atom_id *)a < *(atom_id *)b) return -1;
+ if (*(atom_id *)a > *(atom_id *)b) return 1;
+ return 0;
+}
+
+/*!
+ * \param[in,out] g Index group to be sorted.
+ */
+void
+gmx_ana_index_sort(gmx_ana_index_t *g)
+{
+ qsort(g->index, g->isize, sizeof(*g->index), cmp_atomid);
+}
+
+/*!
+ * \param[in] a Index group to check.
+ * \param[in] b Index group to check.
+ * \returns TRUE if \p a and \p b are equal, FALSE otherwise.
+ */
+gmx_bool
+gmx_ana_index_equals(gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int i;
+
+ if (a->isize != b->isize)
+ {
+ return FALSE;
+ }
+ for (i = 0; i < a->isize; ++i)
+ {
+ if (a->index[i] != b->index[i])
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*!
+ * \param[in] a Index group to check against.
+ * \param[in] b Index group to check.
+ * \returns TRUE if \p b is contained in \p a,
+ * FALSE otherwise.
+ *
+ * If the elements are not in the same order in both groups, the function
+ * fails. However, the groups do not need to be sorted.
+ */
+gmx_bool
+gmx_ana_index_contains(gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int i, j;
+
+ for (i = j = 0; j < b->isize; ++i, ++j) {
+ while (i < a->isize && a->index[i] != b->index[j])
+ {
+ ++i;
+ }
+ if (i == a->isize)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*!
+ * \param[out] dest Output index group (the intersection of \p a and \p b).
+ * \param[in] a First index group.
+ * \param[in] b Second index group.
+ *
+ * \p dest can be the same as \p a or \p b.
+ */
+void
+gmx_ana_index_intersection(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int i, j, k;
+
+ for (i = j = k = 0; i < a->isize && j < b->isize; ++i) {
+ while (j < b->isize && b->index[j] < a->index[i])
+ {
+ ++j;
+ }
+ if (j < b->isize && b->index[j] == a->index[i])
+ {
+ dest->index[k++] = b->index[j++];
+ }
+ }
+ dest->isize = k;
+}
+
+/*!
+ * \param[out] dest Output index group (the difference \p a - \p b).
+ * \param[in] a First index group.
+ * \param[in] b Second index group.
+ *
+ * \p dest can equal \p a, but not \p b.
+ */
+void
+gmx_ana_index_difference(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int i, j, k;
+
+ for (i = j = k = 0; i < a->isize; ++i)
+ {
+ while (j < b->isize && b->index[j] < a->index[i])
+ {
+ ++j;
+ }
+ if (j == b->isize || b->index[j] != a->index[i])
+ {
+ dest->index[k++] = a->index[i];
+ }
+ }
+ dest->isize = k;
+}
+
+/*!
+ * \param[in] a First index group.
+ * \param[in] b Second index group.
+ * \returns Size of the difference \p a - \p b.
+ */
+int
+gmx_ana_index_difference_size(gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int i, j, k;
+
+ for (i = j = k = 0; i < a->isize; ++i)
+ {
+ while (j < b->isize && b->index[j] < a->index[i])
+ {
+ ++j;
+ }
+ if (j == b->isize || b->index[j] != a->index[i])
+ {
+ ++k;
+ }
+ }
+ return k;
+}
+
+/*!
+ * \param[out] dest1 Output group 1 (will equal \p g).
+ * \param[out] dest2 Output group 2 (will equal \p src - \p g).
+ * \param[in] src Group to be partitioned.
+ * \param[in] g One partition.
+ *
+ * \pre \p g is a subset of \p src and both sets are sorted
+ * \pre \p dest1 has allocated storage to store \p src
+ * \post \p dest1 == \p g
+ * \post \p dest2 == \p src - \p g
+ *
+ * No storage should be allocated for \p dest2; after the call,
+ * \p dest2->index points to the memory allocated for \p dest1
+ * (to a part that is not used by \p dest1).
+ *
+ * The calculation can be performed in-place by setting \p dest1 equal to
+ * \p src.
+ */
+void
+gmx_ana_index_partition(gmx_ana_index_t *dest1, gmx_ana_index_t *dest2,
+ gmx_ana_index_t *src, gmx_ana_index_t *g)
+
+{
+ int i, j, k;
+
+ dest2->index = dest1->index + g->isize;
+ dest2->isize = src->isize - g->isize;
+ for (i = g->isize-1, j = src->isize-1, k = dest2->isize-1; i >= 0; --i, --j)
+ {
+ while (j >= 0 && src->index[j] != g->index[i])
+ {
+ dest2->index[k--] = src->index[j--];
+ }
+ }
+ while (j >= 0)
+ {
+ dest2->index[k--] = src->index[j--];
+ }
+ gmx_ana_index_copy(dest1, g, FALSE);
+}
+
+/*!
+ * \param[out] dest Output index group (the union of \p a and \p b).
+ * \param[in] a First index group.
+ * \param[in] b Second index group.
+ *
+ * \p a and \p b can have common items.
+ * \p dest can equal \p a or \p b.
+ *
+ * \see gmx_ana_index_merge()
+ */
+void
+gmx_ana_index_union(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int dsize;
+ int i, j, k;
+
+ dsize = gmx_ana_index_difference_size(b, a);
+ i = a->isize - 1;
+ j = b->isize - 1;
+ dest->isize = a->isize + dsize;
+ for (k = dest->isize - 1; k >= 0; k--)
+ {
+ if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
+ {
+ dest->index[k] = b->index[j--];
+ }
+ else
+ {
+ if (j >= 0 && a->index[i] == b->index[j])
+ {
+ --j;
+ }
+ dest->index[k] = a->index[i--];
+ }
+ }
+}
+
+/*!
+ * \param[out] dest Output index group (the union of \p a and \p b).
+ * \param[in] a First index group.
+ * \param[in] b Second index group.
+ *
+ * \p a and \p b should not have common items.
+ * \p dest can equal \p a or \p b.
+ *
+ * \see gmx_ana_index_union()
+ */
+void
+gmx_ana_index_merge(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b)
+{
+ int i, j, k;
+
+ i = a->isize - 1;
+ j = b->isize - 1;
+ dest->isize = a->isize + b->isize;
+ for (k = dest->isize - 1; k >= 0; k--)
+ {
+ if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
+ {
+ dest->index[k] = b->index[j--];
+ }
+ else
+ {
+ dest->index[k] = a->index[i--];
+ }
+ }
+}
+
+/********************************************************************
+ * gmx_ana_indexmap_t and related things
+ ********************************************************************/
+
+/*!
+ * \param[in,out] t Output block.
+ * \param[in] top Topology structure
+ * (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL
+ * otherwise).
+ * \param[in] g Index group
+ * (can be NULL if \p type is \ref INDEX_UNKNOWN).
+ * \param[in] type Type of partitioning to make.
+ * \param[in] bComplete
+ * If TRUE, the index group is expanded to include any residue/molecule
+ * (depending on \p type) that is partially contained in the group.
+ * If \p type is not INDEX_RES or INDEX_MOL, this has no effect.
+ *
+ * \p m should have been initialized somehow (calloc() is enough) unless
+ * \p type is INDEX_UNKNOWN.
+ * \p g should be sorted.
+ */
+void
+gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g,
+ e_index_t type, gmx_bool bComplete)
+{
+ int i, j, ai;
+ int id, cur;
+
+ if (type == INDEX_UNKNOWN)
+ {
+ t->nr = 1;
+ snew(t->index, 2);
+ t->nalloc_index = 2;
+ t->index[0] = 0;
+ t->index[1] = 0;
+ t->nra = 0;
+ t->a = NULL;
+ t->nalloc_a = 0;
+ return;
+ }
+
+ /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it
+ * off otherwise. */
+ if (type != INDEX_RES && type != INDEX_MOL)
+ {
+ bComplete = FALSE;
+ }
+ /* Allocate memory for the atom array and fill it unless we are using
+ * completion. */
+ if (bComplete)
+ {
+ t->nra = 0;
+ /* We may allocate some extra memory here because we don't know in
+ * advance how much will be needed. */
+ if (t->nalloc_a < top->atoms.nr)
+ {
+ srenew(t->a, top->atoms.nr);
+ t->nalloc_a = top->atoms.nr;
+ }
+ }
+ else
+ {
+ t->nra = g->isize;
+ if (t->nalloc_a < g->isize)
+ {
+ srenew(t->a, g->isize);
+ t->nalloc_a = g->isize;
+ }
+ memcpy(t->a, g->index, g->isize*sizeof(*(t->a)));
+ }
+
+ /* Allocate memory for the block index. We don't know in advance
+ * how much will be needed, so we allocate some extra and free it in the
+ * end. */
+ if (t->nalloc_index < g->isize + 1)
+ {
+ srenew(t->index, g->isize + 1);
+ t->nalloc_index = g->isize + 1;
+ }
+ /* Clear counters */
+ t->nr = 0;
+ j = 0; /* j is used by residue completion for the first atom not stored */
+ id = cur = -1;
+ for (i = 0; i < g->isize; ++i)
+ {
+ ai = g->index[i];
+ /* Find the ID number of the atom/residue/molecule corresponding to
+ * atom ai. */
+ switch (type)
+ {
+ case INDEX_ATOM:
+ id = ai;
+ break;
+ case INDEX_RES:
+ id = top->atoms.atom[ai].resind;
+ break;
+ case INDEX_MOL:
+ while (ai >= top->mols.index[id+1])
+ {
+ id++;
+ }
+ break;
+ case INDEX_UNKNOWN: /* Should not occur */
+ case INDEX_ALL:
+ id = 0;
+ break;
+ }
+ /* If this is the first atom in a new block, initialize the block. */
+ if (id != cur)
+ {
+ if (bComplete)
+ {
+ /* For completion, we first set the start of the block. */
+ t->index[t->nr++] = t->nra;
+ /* And then we find all the atoms that should be included. */
+ switch (type)
+ {
+ case INDEX_RES:
+ while (top->atoms.atom[j].resind != id)
+ {
+ ++j;
+ }
+ while (j < top->atoms.nr && top->atoms.atom[j].resind == id)
+ {
+ t->a[t->nra++] = j;
+ ++j;
+ }
+ break;
+
+ case INDEX_MOL:
+ for (j = top->mols.index[id]; j < top->mols.index[id+1]; ++j)
+ {
+ t->a[t->nra++] = j;
+ }
+ break;
+
+ default: /* Should not be reached */
+ gmx_bug("internal error");
+ break;
+ }
+ }
+ else
+ {
+ /* If not using completion, simply store the start of the block. */
+ t->index[t->nr++] = i;
+ }
+ cur = id;
+ }
+ }
+ /* Set the end of the last block */
+ t->index[t->nr] = t->nra;
+ /* Free any unnecessary memory */
+ srenew(t->index, t->nr+1);
+ t->nalloc_index = t->nr+1;
+ if (bComplete)
+ {
+ srenew(t->a, t->nra);
+ t->nalloc_a = t->nra;
+ }
+}
+
+/*!
+ * \param[in] g Index group to check.
+ * \param[in] b Block data to check against.
+ * \returns TRUE if \p g consists of one or more complete blocks from \p b,
+ * FALSE otherwise.
+ *
+ * The atoms in \p g are assumed to be sorted.
+ */
+gmx_bool
+gmx_ana_index_has_full_blocks(gmx_ana_index_t *g, t_block *b)
+{
+ int i, j, bi;
+
+ i = bi = 0;
+ /* Each round in the loop matches one block */
+ while (i < g->isize)
+ {
+ /* Find the block that begins with the first unmatched atom */
+ while (bi < b->nr && b->index[bi] != g->index[i])
+ {
+ ++bi;
+ }
+ /* If not found, or if too large, return */
+ if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
+ {
+ return FALSE;
+ }
+ /* Check that the block matches the index */
+ for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
+ {
+ if (g->index[i] != j)
+ {
+ return FALSE;
+ }
+ }
+ /* Move the search to the next block */
+ ++bi;
+ }
+ return TRUE;
+}
+
+/*!
+ * \param[in] g Index group to check.
+ * \param[in] b Block data to check against.
+ * \returns TRUE if \p g consists of one or more complete blocks from \p b,
+ * FALSE otherwise.
+ *
+ * The atoms in \p g and \p b->a are assumed to be in the same order.
+ */
+gmx_bool
+gmx_ana_index_has_full_ablocks(gmx_ana_index_t *g, t_blocka *b)
+{
+ int i, j, bi;
+
+ i = bi = 0;
+ /* Each round in the loop matches one block */
+ while (i < g->isize)
+ {
+ /* Find the block that begins with the first unmatched atom */
+ while (bi < b->nr && b->a[b->index[bi]] != g->index[i])
+ {
+ ++bi;
+ }
+ /* If not found, or if too large, return */
+ if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
+ {
+ return FALSE;
+ }
+ /* Check that the block matches the index */
+ for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
+ {
+ if (b->a[j] != g->index[i])
+ {
+ return FALSE;
+ }
+ }
+ /* Move the search to the next block */
+ ++bi;
+ }
+ return TRUE;
+}
+
+/*!
+ * \param[in] g Index group to check.
+ * \param[in] type Block data to check against.
+ * \param[in] top Topology data.
+ * \returns TRUE if \p g consists of one or more complete elements of type
+ * \p type, FALSE otherwise.
+ *
+ * If \p type is \ref INDEX_ATOM, the return value is always TRUE.
+ * If \p type is \ref INDEX_UNKNOWN or \ref INDEX_ALL, the return value is
+ * always FALSE.
+ */
+gmx_bool
+gmx_ana_index_has_complete_elems(gmx_ana_index_t *g, e_index_t type,
+ t_topology *top)
+{
+ switch (type)
+ {
+ case INDEX_UNKNOWN:
+ case INDEX_ALL:
+ return FALSE;
+
+ case INDEX_ATOM:
+ return TRUE;
+
+ case INDEX_RES:
+ {
+ int i, ai;
+ int id, prev;
+
+ prev = -1;
+ for (i = 0; i < g->isize; ++i)
+ {
+ ai = g->index[i];
+ id = top->atoms.atom[ai].resind;
+ if (id != prev)
+ {
+ if (ai > 0 && top->atoms.atom[ai-1].resind == id)
+ {
+ return FALSE;
+ }
+ if (i > 0 && g->index[i-1] < top->atoms.nr - 1
+ && top->atoms.atom[g->index[i-1]+1].resind == prev)
+ {
+ return FALSE;
+ }
+ }
+ prev = id;
+ }
+ if (g->index[i-1] < top->atoms.nr - 1
+ && top->atoms.atom[g->index[i-1]+1].resind == prev)
+ {
+ return FALSE;
+ }
+ break;
+ }
+
+ case INDEX_MOL:
+ return gmx_ana_index_has_full_blocks(g, &top->mols);
+ }
+ return TRUE;
+}
+
+/*!
+ * \param[out] m Output structure.
+ *
+ * Any contents of \p m are discarded without freeing.
+ */
+void
+gmx_ana_indexmap_clear(gmx_ana_indexmap_t *m)
+{
+ m->type = INDEX_UNKNOWN;
+ m->nr = 0;
+ m->refid = NULL;
+ m->mapid = NULL;
+ m->mapb.nr = 0;
+ m->mapb.index = NULL;
+ m->mapb.nalloc_index = 0;
+ m->orgid = NULL;
+ m->b.nr = 0;
+ m->b.index = NULL;
+ m->b.nra = 0;
+ m->b.a = NULL;
+ m->b.nalloc_index = 0;
+ m->b.nalloc_a = 0;
+ m->bStatic = TRUE;
+ m->bMapStatic = TRUE;
+}
+
+/*!
+ * \param[in,out] m Mapping structure.
+ * \param[in] nr Maximum number of blocks to reserve space for.
+ * \param[in] isize Maximum number of atoms to reserve space for.
+ */
+void
+gmx_ana_indexmap_reserve(gmx_ana_indexmap_t *m, int nr, int isize)
+{
+ if (m->mapb.nalloc_index < nr + 1)
+ {
+ srenew(m->refid, nr);
+ srenew(m->mapid, nr);
+ srenew(m->orgid, nr);
+ srenew(m->mapb.index, nr + 1);
+ srenew(m->b.index, nr + 1);
+ m->mapb.nalloc_index = nr + 1;
+ m->b.nalloc_index = nr + 1;
+ }
+ if (m->b.nalloc_a < isize)
+ {
+ srenew(m->b.a, isize);
+ m->b.nalloc_a = isize;
+ }
+}
+
+/*!
+ * \param[in,out] m Mapping structure to initialize.
+ * \param[in] g Index group to map
+ * (can be NULL if \p type is \ref INDEX_UNKNOWN).
+ * \param[in] top Topology structure
+ * (can be NULL if \p type is not \ref INDEX_RES or \ref INDEX_MOL).
+ * \param[in] type Type of mapping to construct.
+ *
+ * Initializes a new index group mapping.
+ * The index group provided to gmx_ana_indexmap_update() should always be a
+ * subset of the \p g given here.
+ *
+ * \p m should have been initialized somehow (calloc() is enough).
+ */
+void
+gmx_ana_indexmap_init(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
+ t_topology *top, e_index_t type)
+{
+ int i, ii, mi;
+
+ m->type = type;
+ gmx_ana_index_make_block(&m->b, top, g, type, FALSE);
+ gmx_ana_indexmap_reserve(m, m->b.nr, m->b.nra);
+ m->nr = m->b.nr;
+ for (i = mi = 0; i < m->nr; ++i)
+ {
+ ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
+ switch (type)
+ {
+ case INDEX_ATOM:
+ m->orgid[i] = ii;
+ break;
+ case INDEX_RES:
+ m->orgid[i] = top->atoms.atom[ii].resind;
+ break;
+ case INDEX_MOL:
+ while (top->mols.index[mi+1] <= ii)
+ {
+ ++mi;
+ }
+ m->orgid[i] = mi;
+ break;
+ case INDEX_ALL:
+ case INDEX_UNKNOWN:
+ m->orgid[i] = 0;
+ break;
+ }
+ }
+ for (i = 0; i < m->nr; ++i)
+ {
+ m->refid[i] = i;
+ m->mapid[i] = m->orgid[i];
+ }
+ m->mapb.nr = m->nr;
+ memcpy(m->mapb.index, m->b.index, (m->nr+1)*sizeof(*(m->mapb.index)));
+ m->bStatic = TRUE;
+ m->bMapStatic = TRUE;
+}
+
+/*!
+ * \param[in,out] m Mapping structure to initialize.
+ * \param[in] b Block information to use for data.
+ *
+ * Frees some memory that is not necessary for static index group mappings.
+ * Internal pointers are set to point to data in \p b; it is the responsibility
+ * of the caller to ensure that the block information matches the contents of
+ * the mapping.
+ * After this function has been called, the index group provided to
+ * gmx_ana_indexmap_update() should always be the same as \p g given here.
+ *
+ * This function breaks modularity of the index group mapping interface in an
+ * ugly way, but allows reducing memory usage of static selections by a
+ * significant amount.
+ */
+void
+gmx_ana_indexmap_set_static(gmx_ana_indexmap_t *m, t_blocka *b)
+{
+ sfree(m->mapid);
+ m->mapid = m->orgid;
+ sfree(m->b.index);
+ m->b.nalloc_index = 0;
+ m->b.index = b->index;
+ sfree(m->mapb.index);
+ m->mapb.nalloc_index = 0;
+ m->mapb.index = m->b.index;
+ sfree(m->b.a);
+ m->b.nalloc_a = 0;
+ m->b.a = b->a;
+}
+
+/*!
+ * \param[in,out] dest Destination data structure.
+ * \param[in] src Source mapping.
+ * \param[in] bFirst If TRUE, memory is allocated for \p dest and a full
+ * copy is made; otherwise, only variable parts are copied, and no memory
+ * is allocated.
+ *
+ * \p dest should have been initialized somehow (calloc() is enough).
+ */
+void
+gmx_ana_indexmap_copy(gmx_ana_indexmap_t *dest, gmx_ana_indexmap_t *src, gmx_bool bFirst)
+{
+ if (bFirst)
+ {
+ gmx_ana_indexmap_reserve(dest, src->b.nr, src->b.nra);
+ dest->type = src->type;
+ dest->b.nr = src->b.nr;
+ dest->b.nra = src->b.nra;
+ memcpy(dest->orgid, src->orgid, dest->b.nr*sizeof(*dest->orgid));
+ memcpy(dest->b.index, src->b.index, (dest->b.nr+1)*sizeof(*dest->b.index));
+ memcpy(dest->b.a, src->b.a, dest->b.nra*sizeof(*dest->b.a));
+ }
+ dest->nr = src->nr;
+ dest->mapb.nr = src->mapb.nr;
+ memcpy(dest->refid, src->refid, dest->nr*sizeof(*dest->refid));
+ memcpy(dest->mapid, src->mapid, dest->nr*sizeof(*dest->mapid));
+ memcpy(dest->mapb.index, src->mapb.index,(dest->mapb.nr+1)*sizeof(*dest->mapb.index));
+ dest->bStatic = src->bStatic;
+ dest->bMapStatic = src->bMapStatic;
+}
+
+/*!
+ * \param[in,out] m Mapping structure.
+ * \param[in] g Current index group.
+ * \param[in] bMaskOnly TRUE if the unused blocks should be masked with
+ * -1 instead of removing them.
+ *
+ * Updates the index group mapping with the new index group \p g.
+ *
+ * \see gmx_ana_indexmap_t
+ */
+void
+gmx_ana_indexmap_update(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
+ gmx_bool bMaskOnly)
+{
+ int i, j, bi, bj;
+ gmx_bool bStatic;
+
+ /* Process the simple cases first */
+ if (m->type == INDEX_UNKNOWN && m->b.nra == 0)
+ {
+ return;
+ }
+ if (m->type == INDEX_ALL)
+ {
+ if (m->b.nr > 0)
+ {
+ m->mapb.index[1] = g->isize;
+ }
+ return;
+ }
+ /* Reset the reference IDs and mapping if necessary */
+ bStatic = (g->isize == m->b.nra && m->nr == m->b.nr);
+ if (bStatic || bMaskOnly)
+ {
+ if (!m->bStatic)
+ {
+ for (bj = 0; bj < m->b.nr; ++bj)
+ {
+ m->refid[bj] = bj;
+ }
+ }
+ if (!m->bMapStatic)
+ {
+ for (bj = 0; bj < m->b.nr; ++bj)
+ {
+ m->mapid[bj] = m->orgid[bj];
+ }
+ for (bj = 0; bj <= m->b.nr; ++bj)
+ {
+ m->mapb.index[bj] = m->b.index[bj];
+ }
+ m->bMapStatic = TRUE;
+ }
+ }
+ /* Exit immediately if the group is static */
+ if (bStatic)
+ {
+ m->bStatic = TRUE;
+ return;
+ }
+
+ if (bMaskOnly)
+ {
+ m->nr = m->b.nr;
+ for (i = j = bj = 0; i < g->isize; ++i, ++j)
+ {
+ /* Find the next atom in the block */
+ while (m->b.a[j] != g->index[i])
+ {
+ ++j;
+ }
+ /* Mark blocks that did not contain any atoms */
+ while (bj < m->b.nr && m->b.index[bj+1] <= j)
+ {
+ m->refid[bj++] = -1;
+ }
+ /* Advance the block index if we have reached the next block */
+ if (m->b.index[bj] <= j)
+ {
+ ++bj;
+ }
+ }
+ /* Mark the last blocks as not accessible */
+ while (bj < m->b.nr)
+ {
+ m->refid[bj++] = -1;
+ }
+ }
+ else
+ {
+ for (i = j = bi = 0, bj = -1; i < g->isize; ++i)
+ {
+ /* Find the next atom in the block */
+ while (m->b.a[j] != g->index[i])
+ {
+ ++j;
+ }
+ /* If we have reached a new block, add it */
+ if (m->b.index[bj+1] <= j)
+ {
+ /* Skip any blocks in between */
+ while (bj < m->b.nr && m->b.index[bj+1] <= j)
+ {
+ ++bj;
+ }
+ m->refid[bi] = bj;
+ m->mapid[bi] = m->orgid[bj];
+ m->mapb.index[bi] = i;
+ bi++;
+ }
+ }
+ /* Update the number of blocks */
+ m->mapb.index[bi] = g->isize;
+ m->nr = bi;
+ m->bMapStatic = FALSE;
+ }
+ m->mapb.nr = m->nr;
+ m->bStatic = FALSE;
+}
+
+/*!
+ * \param[in,out] m Mapping structure to free.
+ *
+ * All the memory allocated for the mapping structure is freed, and
+ * the pointers set to NULL.
+ * The pointer \p m is not freed.
+ */
+void
+gmx_ana_indexmap_deinit(gmx_ana_indexmap_t *m)
+{
+ sfree(m->refid);
+ if (m->mapid != m->orgid)
+ {
+ sfree(m->mapid);
+ }
+ if (m->mapb.nalloc_index > 0)
+ {
+ sfree(m->mapb.index);
+ }
+ sfree(m->orgid);
+ if (m->b.nalloc_index > 0)
+ {
+ sfree(m->b.index);
+ }
+ if (m->b.nalloc_a > 0)
+ {
+ sfree(m->b.a);
+ }
+ gmx_ana_indexmap_clear(m);
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief API for handling index files and index groups.
+ *
+ * The API contains functions and data structures for handling index
+ * files more conveniently than as several separate variables.
+ * In addition to basic functions for initializing the data structures and
+ * making copies, functions are provided for performing (most) set operations
+ * on sorted index groups.
+ * There is also a function for partitioning a index group based on
+ * topology information such as residues or molecules.
+ * Finally, there is a set of functions for constructing mappings between
+ * an index group and its subgroups such.
+ * These can be used with dynamic index group in calculations if one
+ * needs to have a unique ID for each possible atom/residue/molecule in the
+ * selection, e.g., for analysis of dynamics or for look-up tables.
+ *
+ * Mostly, these functions are used internally by the library and the
+ * selection engine.
+ * However, some of the checking functions can be useful in user code to
+ * check the validity of input groups.
+ * Also, the mapping functions are useful when dealing with dynamic index
+ * groups.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_INDEXUTIL_H
+#define GMX_SELECTION_INDEXUTIL_H
+
+#include "../legacyheaders/typedefs.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Stores a set of index groups. */
+typedef struct gmx_ana_indexgrps_t gmx_ana_indexgrps_t;
+
+/*! \brief
+ * Specifies the type of index partition or index mapping in several contexts.
+ *
+ * \see gmx_ana_index_make_block(), gmx_ana_indexmap_init()
+ */
+typedef enum
+{
+ INDEX_UNKNOWN, /**< Unknown index type.*/
+ INDEX_ATOM, /**< Each atom in a separate block.*/
+ INDEX_RES, /**< Each residue in a separate block.*/
+ INDEX_MOL, /**< Each molecule in a separate block.*/
+ INDEX_ALL /**< All atoms in a single block.*/
+} e_index_t;
+
+/*! \brief
+ * Stores a single index group.
+ */
+typedef struct gmx_ana_index_t
+{
+ /** Number of atoms. */
+ int isize;
+ /** List of atoms. */
+ atom_id *index;
+ /** Group name. */
+ char *name;
+ /** Number of items allocated for \p index. */
+ int nalloc_index;
+} gmx_ana_index_t;
+
+/*! \brief
+ * Data structure for calculating index group mappings.
+ */
+typedef struct gmx_ana_indexmap_t
+{
+ /** Type of the mapping. */
+ e_index_t type;
+ /*! \brief
+ * Current number of mapped values.
+ *
+ * This is the current number of values in the \p refid and \p mapid
+ * arrays.
+ * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), this
+ * is always equal to \p b.nr, i.e., the number of blocks in the
+ * original index group.
+ */
+ int nr;
+ /*! \brief
+ * Current reference IDs.
+ *
+ * This array provides a mapping from the current index group (last given
+ * to gmx_ana_indexmap_update()) to the blocks in \p b, i.e., the
+ * original index group used in gmx_ana_indexmap_init().
+ * The mapping is zero-based.
+ * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), the indices
+ * for blocks not present in the current group are set to -1, otherwise
+ * they are removed completely and the \p nr field updated.
+ */
+ int *refid;
+ /*! \brief
+ * Current mapped IDs.
+ *
+ * This array provides an arbitrary mapping from the current index group
+ * to the original index group. Instead of a zero-based mapping, the
+ * values from the \p orgid array are used. That is,
+ * \c mapid[i]=orgid[refid[i]].
+ * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), this array
+ * equals \p orgid.
+ */
+ int *mapid;
+ /*! \brief
+ * Mapped block structure.
+ *
+ * A block structure that corresponds to the current index group.
+ */
+ t_block mapb;
+
+ /*! \brief
+ * Arbitrary ID numbers for the blocks.
+ *
+ * This array has \p b.nr elements, each defining an ID number for a
+ * block in \p b.
+ * These are initialized in gmx_ana_indexmap_init() based on the type:
+ * - \ref INDEX_ATOM : the atom indices
+ * - \ref INDEX_RES : the residue numbers
+ * - \ref INDEX_MOL : the molecule numbers
+ *
+ * All the above numbers are zero-based.
+ * After gmx_ana_indexmap_init(), the user is free to change these values
+ * if the above are not appropriate.
+ * The mapped values can be read through \p mapid.
+ */
+ int *orgid;
+
+ /*! \brief
+ * Block data that defines the mapping (internal use only).
+ *
+ * The data is initialized by gmx_ana_indexmap_init() and is not changed
+ * after that.
+ * Hence, it cannot be directly applied to the index group passed to
+ * gmx_ana_indexmap_update() unless \p bMaskOnly was specified or the
+ * index group is identical to the one provided to gmx_ana_indexmap_init().
+ */
+ t_blocka b;
+ /*! \brief
+ * TRUE if the current reference IDs are for the whole group (internal use only).
+ *
+ * This is used internally to optimize the evaluation such that
+ * gmx_ana_indexmap_update() does not take any time if the group is
+ * actually static.
+ */
+ gmx_bool bStatic;
+ /*! \brief
+ * TRUE if the current mapping is for the whole group (internal use only).
+ *
+ * This is used internally to optimize the evaluation such that
+ * gmx_ana_indexmap_update() does not take any time if the group is
+ * actually static.
+ */
+ gmx_bool bMapStatic;
+} gmx_ana_indexmap_t;
+
+
+/*! \name Functions for handling gmx_ana_indexgrps_t
+ */
+/*@{*/
+/** Allocate memory for index groups. */
+void
+gmx_ana_indexgrps_alloc(gmx_ana_indexgrps_t **g, int ngrps);
+/** Initializes index groups from arrays. */
+void
+gmx_ana_indexgrps_set(gmx_ana_indexgrps_t **g, int ngrps, int *isize,
+ atom_id **index, char **name, gmx_bool bFree);
+/** Reads index groups from a file or constructs them from topology. */
+void
+gmx_ana_indexgrps_init(gmx_ana_indexgrps_t **g, t_topology *top,
+ const char *fnm);
+/** Ask user to select index groups, possibly constructing groups from
+ * topology. */
+void
+gmx_ana_indexgrps_get(gmx_ana_indexgrps_t **g, t_topology *top,
+ const char *fnm, int ngrps);
+/** Ask user to select index groups from those specified in a file. */
+void
+gmx_ana_indexgrps_rd(gmx_ana_indexgrps_t **g, const char *fnm, int ngrps);
+/** Frees memory allocated for index groups. */
+void
+gmx_ana_indexgrps_free(gmx_ana_indexgrps_t *g);
+/** Create a deep copy of \c gmx_ana_indexgrps_t. */
+void
+gmx_ana_indexgrps_clone(gmx_ana_indexgrps_t **dest, gmx_ana_indexgrps_t *src);
+/** Returns TRUE if the index group structure is emtpy. */
+gmx_bool
+gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t *g);
+
+/** Returns a pointer to an index group. */
+gmx_ana_index_t *
+gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t *g, int n);
+/** Extracts a single index group. */
+gmx_bool
+gmx_ana_indexgrps_extract(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, int n);
+/** Finds and extracts a single index group by name. */
+gmx_bool
+gmx_ana_indexgrps_find(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, char *name);
+
+/** Writes out a list of index groups. */
+void
+gmx_ana_indexgrps_print(gmx_ana_indexgrps_t *g, int maxn);
+/*@}*/
+
+/*! \name Functions for handling gmx_ana_index_t
+ */
+/*@{*/
+/** Reserves memory to store an index group of size \p isize. */
+void
+gmx_ana_index_reserve(gmx_ana_index_t *g, int isize);
+/** Frees any memory not necessary to hold the current contents. */
+void
+gmx_ana_index_squeeze(gmx_ana_index_t *g);
+/** Initializes an empty index group. */
+void
+gmx_ana_index_clear(gmx_ana_index_t *g);
+/** Constructs a \c gmx_ana_index_t from given values. */
+void
+gmx_ana_index_set(gmx_ana_index_t *g, int isize, atom_id *index, char *name,
+ int nalloc);
+/** Creates a simple index group from the first to the \p natoms'th atom. */
+void
+gmx_ana_index_init_simple(gmx_ana_index_t *g, int natoms, char *name);
+/** Frees memory allocated for an index group. */
+void
+gmx_ana_index_deinit(gmx_ana_index_t *g);
+/** Copies a \c gmx_ana_index_t. */
+void
+gmx_ana_index_copy(gmx_ana_index_t *dest, gmx_ana_index_t *src, gmx_bool bAlloc);
+
+/** Writes out the contents of a index group. */
+void
+gmx_ana_index_dump(gmx_ana_index_t *g, int i, int maxn);
+
+/** Checks whether all indices are between 0 and \p natoms. */
+void
+gmx_ana_index_check(gmx_ana_index_t *g, int natoms);
+/** Checks whether an index group is sorted. */
+gmx_bool
+gmx_ana_index_check_sorted(gmx_ana_index_t *g);
+/*@}*/
+
+/*! \name Functions for set operations on gmx_ana_index_t
+ */
+/*@{*/
+/** Sorts the indices within an index group. */
+void
+gmx_ana_index_sort(gmx_ana_index_t *g);
+/** Checks whether two index groups are equal. */
+gmx_bool
+gmx_ana_index_equals(gmx_ana_index_t *a, gmx_ana_index_t *b);
+/** Checks whether a sorted index group contains another sorted index group. */
+gmx_bool
+gmx_ana_index_contains(gmx_ana_index_t *a, gmx_ana_index_t *b);
+
+/** Calculates the intersection between two sorted index groups. */
+void
+gmx_ana_index_intersection(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b);
+/** Calculates the set difference between two sorted index groups. */
+void
+gmx_ana_index_difference(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b);
+/** Calculates the size of the difference between two sorted index groups. */
+int
+gmx_ana_index_difference_size(gmx_ana_index_t *a, gmx_ana_index_t *b);
+/** Calculates the union of two sorted index groups. */
+void
+gmx_ana_index_union(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b);
+/** Merges two distinct sorted index groups. */
+void
+gmx_ana_index_merge(gmx_ana_index_t *dest,
+ gmx_ana_index_t *a, gmx_ana_index_t *b);
+/** Calculates the intersection and the difference in one call. */
+void
+gmx_ana_index_partition(gmx_ana_index_t *dest1, gmx_ana_index_t *dest2,
+ gmx_ana_index_t *src, gmx_ana_index_t *g);
+/*@}*/
+
+/*! \name Functions for handling gmx_ana_indexmap_t and related things
+ */
+/*@{*/
+/** Partition a group based on topology information. */
+void
+gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g,
+ e_index_t type, gmx_bool bComplete);
+/** Checks whether a group consists of full blocks. */
+gmx_bool
+gmx_ana_index_has_full_blocks(gmx_ana_index_t *g, t_block *b);
+/** Checks whether a group consists of full blocks. */
+gmx_bool
+gmx_ana_index_has_full_ablocks(gmx_ana_index_t *g, t_blocka *b);
+/** Checks whether a group consists of full residues/molecules. */
+gmx_bool
+gmx_ana_index_has_complete_elems(gmx_ana_index_t *g, e_index_t type, t_topology *top);
+
+/** Initializes an empty index group mapping. */
+void
+gmx_ana_indexmap_clear(gmx_ana_indexmap_t *m);
+/** Reserves memory for an index group mapping. */
+void
+gmx_ana_indexmap_reserve(gmx_ana_indexmap_t *m, int nr, int isize);
+/** Initializes an index group mapping. */
+void
+gmx_ana_indexmap_init(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
+ t_topology *top, e_index_t type);
+/** Sets an index group mapping to be static. */
+void
+gmx_ana_indexmap_set_static(gmx_ana_indexmap_t *m, t_blocka *b);
+/** Frees memory allocated for index group mapping. */
+void
+gmx_ana_indexmap_deinit(gmx_ana_indexmap_t *m);
+/** Makes a deep copy of an index group mapping. */
+void
+gmx_ana_indexmap_copy(gmx_ana_indexmap_t *dest, gmx_ana_indexmap_t *src, gmx_bool bFirst);
+/** Updates an index group mapping. */
+void
+gmx_ana_indexmap_update(gmx_ana_indexmap_t *m, gmx_ana_index_t *g, gmx_bool bMaskOnly);
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Definitions of generic keyword evaluation structures.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_KEYWORDS_H
+#define SELECTION_KEYWORDS_H
+
+struct gmx_ana_selmethod_t;
+struct t_selelem;
+struct t_selexpr_param;
+
+/** Selection method data for comparison expression evaluation. */
+extern struct gmx_ana_selmethod_t sm_compare;
+
+/** Selection method data for integer keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_int;
+/** Selection method data for real keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_real;
+/** Selection method data for string keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_str;
+/** Selection method data for position keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_pos;
+
+/** Prints information about a comparison expression. */
+void
+_gmx_selelem_print_compare_info(FILE *fp, void *data);
+
+/** Sets the position type for position keyword evaluation. */
+void
+_gmx_selelem_set_kwpos_type(struct t_selelem *sel, const char *type);
+/** Sets the flags for position keyword evaluation. */
+void
+_gmx_selelem_set_kwpos_flags(struct t_selelem *sel, int flags);
+
+/** Does custom processing for parameters of the \c same selection method. */
+int
+_gmx_selelem_custom_init_same(struct gmx_ana_selmethod_t **method,
+ struct t_selexpr_param *params, void *scanner);
+
+/** Initializes a selection element for evaluating a keyword in a given group. */
+int
+_gmx_sel_init_keyword_evaluator(struct t_selelem **sel,
+ struct gmx_ana_selmethod_t *method,
+ struct t_selexpr_param *param, void *scanner);
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in mempool.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <gmx_fatal.h>
+#include <smalloc.h>
+
+#include "gromacs/selection/indexutil.h"
+
+#include "mempool.h"
+
+//! Alignment in bytes for all returned blocks.
+#define ALIGN_STEP 8
+
+/*! \internal \brief
+ * Describes a single block allocated from the memory pool.
+ */
+typedef struct gmx_sel_mempool_block_t
+{
+ //! Pointer to the start of the block (as returned to the user).
+ void *ptr;
+ //! Size of the block, including padding required to align next block.
+ size_t size;
+} gmx_sel_mempool_block_t;
+
+/*! \internal \brief
+ * Describes a memory pool.
+ */
+struct gmx_sel_mempool_t
+{
+ //! Number of bytes currently allocated from the pool.
+ size_t currsize;
+ //! Number of bytes free in the pool, or 0 if \a buffer is NULL.
+ size_t freesize;
+ //! Memory area allocated for the pool, or NULL if not yet reserved.
+ char *buffer;
+ //! Pointer to the first free byte (aligned at ::ALIGN_STEP) in \a buffer.
+ char *freeptr;
+ //! Number of blocks allocated from the pool.
+ int nblocks;
+ //! Array describing the allocated blocks.
+ gmx_sel_mempool_block_t *blockstack;
+ //! Number of elements allocated for the \a blockstack array.
+ int blockstack_nalloc;
+ /*! \brief
+ * Maximum number of bytes that have been reserved from the pool
+ * simultaneously.
+ */
+ size_t maxsize;
+};
+
+int
+_gmx_sel_mempool_create(gmx_sel_mempool_t **mpp)
+{
+ gmx_sel_mempool_t *mp;
+
+ snew(mp, 1);
+ mp->currsize = 0;
+ mp->freesize = 0;
+ mp->buffer = NULL;
+ mp->freeptr = NULL;
+ mp->nblocks = 0;
+ mp->blockstack = NULL;
+ mp->blockstack_nalloc = 0;
+ mp->maxsize = 0;
+ *mpp = mp;
+ return 0;
+}
+
+void
+_gmx_sel_mempool_destroy(gmx_sel_mempool_t *mp)
+{
+ if (!mp->buffer)
+ {
+ int i;
+
+ for (i = 0; i < mp->nblocks; ++i)
+ {
+ sfree(mp->blockstack[i].ptr);
+ }
+ }
+ sfree(mp->buffer);
+ sfree(mp->blockstack);
+ sfree(mp);
+}
+
+int
+_gmx_sel_mempool_alloc(gmx_sel_mempool_t *mp, void **ptrp, size_t size)
+{
+ void *ptr = NULL;
+ size_t size_walign;
+
+ *ptrp = NULL;
+ size_walign = ((size + ALIGN_STEP - 1) / ALIGN_STEP) * ALIGN_STEP;
+ if (mp->buffer)
+ {
+ if (mp->freesize < size)
+ {
+ gmx_bug("out of memory pool memory");
+ return ENOMEM;
+ }
+ ptr = mp->freeptr;
+ mp->freeptr += size_walign;
+ mp->freesize -= size_walign;
+ mp->currsize += size_walign;
+ }
+ else
+ {
+ ptr = malloc(size);
+ if (!ptr)
+ {
+ gmx_mem("out of memory");
+ return ENOMEM;
+ }
+ mp->currsize += size_walign;
+ if (mp->currsize > mp->maxsize)
+ {
+ mp->maxsize = mp->currsize;
+ }
+ }
+
+ if (mp->nblocks >= mp->blockstack_nalloc)
+ {
+ mp->blockstack_nalloc = mp->nblocks + 10;
+ srenew(mp->blockstack, mp->blockstack_nalloc);
+ }
+ mp->blockstack[mp->nblocks].ptr = ptr;
+ mp->blockstack[mp->nblocks].size = size_walign;
+ mp->nblocks++;
+
+ *ptrp = ptr;
+ return 0;
+}
+
+void
+_gmx_sel_mempool_free(gmx_sel_mempool_t *mp, void *ptr)
+{
+ int size;
+
+ if (ptr == NULL)
+ {
+ return;
+ }
+ assert(mp->nblocks > 0 && mp->blockstack[mp->nblocks - 1].ptr == ptr);
+ mp->nblocks--;
+ size = mp->blockstack[mp->nblocks].size;
+ mp->currsize -= size;
+ if (mp->buffer)
+ {
+ mp->freeptr = (char *)ptr;
+ mp->freesize += size;
+ }
+ else
+ {
+ sfree(ptr);
+ }
+}
+
+int
+_gmx_sel_mempool_reserve(gmx_sel_mempool_t *mp, size_t size)
+{
+ assert(mp->nblocks == 0 && !mp->buffer);
+ if (size == 0)
+ {
+ size = mp->maxsize;
+ }
+ mp->buffer = (char *)malloc(size);
+ if (!mp->buffer)
+ {
+ gmx_mem("out of memory");
+ return ENOMEM;
+ }
+ mp->freesize = size;
+ mp->freeptr = mp->buffer;
+ return 0;
+}
+
+int
+_gmx_sel_mempool_alloc_group(gmx_sel_mempool_t *mp, gmx_ana_index_t *g,
+ int isize)
+{
+ return _gmx_sel_mempool_alloc(mp, (void **)&g->index,
+ sizeof(*g->index)*isize);
+}
+
+void
+_gmx_sel_mempool_free_group(gmx_sel_mempool_t *mp, gmx_ana_index_t *g)
+{
+ _gmx_sel_mempool_free(mp, g->index);
+ g->index = NULL;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Declarations for memory pooling functions.
+ *
+ * \todo
+ * Document these functions.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_MEMPOOL_H
+#define GMX_SELECTION_MEMPOOL_H
+
+struct gmx_ana_index_t;
+
+/** Opaque struct for memory pooling. */
+typedef struct gmx_sel_mempool_t gmx_sel_mempool_t;
+
+/** Create an empty memory pool. */
+int
+_gmx_sel_mempool_create(gmx_sel_mempool_t **mpp);
+/** Destroy a memory pool. */
+void
+_gmx_sel_mempool_destroy(gmx_sel_mempool_t *mp);
+
+/** Allocate memory from a memory pool. */
+int
+_gmx_sel_mempool_alloc(gmx_sel_mempool_t *mp, void **ptrp, size_t size);
+/** Release memory allocated from a memory pool. */
+void
+_gmx_sel_mempool_free(gmx_sel_mempool_t *mp, void *ptr);
+/** Set the size of a memory pool. */
+int
+_gmx_sel_mempool_reserve(gmx_sel_mempool_t *mp, size_t size);
+
+/** Convenience function for allocating an index group from a memory pool. */
+int
+_gmx_sel_mempool_alloc_group(gmx_sel_mempool_t *mp, struct gmx_ana_index_t *g,
+ int isize);
+/** Convenience function for freeing an index group from a memory pool. */
+void
+_gmx_sel_mempool_free_group(gmx_sel_mempool_t *mp, struct gmx_ana_index_t *g);
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \page nbsearch Neighborhood search routines
+ *
+ * Functions to find particles within a neighborhood of a set of particles
+ * are defined in nbsearch.h.
+ * The usage is simple: a data structure is allocated with
+ * gmx_ana_nbsearch_create(), and the box shape and reference positions for a
+ * frame are set using gmx_ana_nbsearch_init() or gmx_ana_nbsearch_pos_init().
+ * Searches can then be performed with gmx_ana_nbsearch_is_within() and
+ * gmx_ana_nbsearch_mindist(), or with versions that take the \c gmx_ana_pos_t
+ * data structure.
+ * When the data structure is no longer required, it can be freed with
+ * gmx_ana_nbsearch_free().
+ *
+ * \internal
+ *
+ * \todo
+ * The grid implementation could still be optimized in several different ways:
+ * - Triclinic grid cells are not the most efficient shape, but make PBC
+ * handling easier.
+ * - Precalculating the required PBC shift for a pair of cells outside the
+ * inner loop. After this is done, it should be quite straightforward to
+ * move to rectangular cells.
+ * - Pruning grid cells from the search list if they are completely outside
+ * the sphere that is being considered.
+ * - A better heuristic could be added for falling back to simple loops for a
+ * small number of reference particles.
+ * - A better heuristic for selecting the grid size.
+ * - A multi-level grid implementation could be used to be able to use small
+ * grids for short cutoffs with very inhomogeneous particle distributions
+ * without a memory cost.
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in nbsearch.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <smalloc.h>
+#include <typedefs.h>
+#include <pbc.h>
+#include <vec.h>
+
+#include "gromacs/selection/nbsearch.h"
+#include "gromacs/selection/position.h"
+
+/*! \internal \brief
+ * Data structure for neighborhood searches.
+ */
+struct gmx_ana_nbsearch_t
+{
+ /** The cutoff. */
+ real cutoff;
+ /** The cutoff squared. */
+ real cutoff2;
+ /** Maximum number of reference points. */
+ int maxnref;
+
+ /** Number of reference points for the current frame. */
+ int nref;
+ /** Reference point positions. */
+ rvec *xref;
+ /** Reference position ids (NULL if not available). */
+ int *refid;
+ /** PBC data. */
+ t_pbc *pbc;
+
+ /** Number of excluded reference positions for current test particle. */
+ int nexcl;
+ /** Exclusions for current test particle. */
+ int *excl;
+
+ /** Whether to try grid searching. */
+ gmx_bool bTryGrid;
+ /** Whether grid searching is actually used for the current positions. */
+ gmx_bool bGrid;
+ /** Array allocated for storing in-unit-cell reference positions. */
+ rvec *xref_alloc;
+ /** FALSE if the box is rectangular. */
+ gmx_bool bTric;
+ /** Box vectors of a single grid cell. */
+ matrix cellbox;
+ /** The reciprocal cell vectors as columns; the inverse of \p cellbox. */
+ matrix recipcell;
+ /** Number of cells along each dimension. */
+ ivec ncelldim;
+ /** Total number of cells. */
+ int ncells;
+ /** Number of reference positions in each cell. */
+ int *ncatoms;
+ /** List of reference positions in each cell. */
+ atom_id **catom;
+ /** Allocation counts for each \p catom[i]. */
+ int *catom_nalloc;
+ /** Allocation count for the per-cell arrays. */
+ int cells_nalloc;
+ /** Number of neighboring cells to consider. */
+ int ngridnb;
+ /** Offsets of the neighboring cells to consider. */
+ ivec *gnboffs;
+ /** Allocation count for \p gnboffs. */
+ int gnboffs_nalloc;
+
+ /** Stores test position during a pair loop. */
+ rvec xtest;
+ /** Stores the previous returned position during a pair loop. */
+ int previ;
+ /** Stores the current exclusion index during loops. */
+ int exclind;
+ /** Stores the test particle cell index during loops. */
+ ivec testcell;
+ /** Stores the current cell neighbor index during pair loops. */
+ int prevnbi;
+ /** Stores the index within the current cell during pair loops. */
+ int prevcai;
+};
+
+/*!
+ * \param[out] data Neighborhood search data structure pointer to initialize.
+ * \param[in] cutoff Cutoff distance for the search
+ * (<=0 stands for no cutoff).
+ * \param[in] maxn Maximum number of reference particles.
+ * \returns 0 on success.
+ */
+int
+gmx_ana_nbsearch_create(gmx_ana_nbsearch_t **data, real cutoff, int maxn)
+{
+ gmx_ana_nbsearch_t *d;
+
+ snew(d, 1);
+ d->bTryGrid = TRUE;
+ if (cutoff <= 0)
+ {
+ cutoff = HUGE_VAL;
+ d->bTryGrid = FALSE;
+ }
+ d->cutoff = cutoff;
+ d->cutoff2 = sqr(cutoff);
+ d->maxnref = maxn;
+
+ d->xref = NULL;
+ d->nexcl = 0;
+ d->exclind = 0;
+
+ d->xref_alloc = NULL;
+ d->ncells = 0;
+ d->ncatoms = NULL;
+ d->catom = NULL;
+ d->catom_nalloc = 0;
+ d->cells_nalloc = 0;
+
+ d->ngridnb = 0;
+ d->gnboffs = NULL;
+ d->gnboffs_nalloc = 0;
+
+ *data = d;
+ return 0;
+}
+
+/*!
+ * \param d Data structure to free.
+ *
+ * After the call, the pointer \p d is no longer valid.
+ */
+void
+gmx_ana_nbsearch_free(gmx_ana_nbsearch_t *d)
+{
+ sfree(d->xref_alloc);
+ sfree(d->ncatoms);
+ if (d->catom)
+ {
+ int ci;
+
+ for (ci = 0; ci < d->ncells; ++ci)
+ {
+ sfree(d->catom[ci]);
+ }
+ sfree(d->catom);
+ }
+ sfree(d->catom_nalloc);
+ sfree(d->gnboffs);
+ sfree(d);
+}
+
+/*! \brief
+ * Calculates offsets to neighboring grid cells that should be considered.
+ *
+ * \param[in,out] d Grid information.
+ * \param[in] pbc Information about the box.
+ */
+static void
+grid_init_cell_nblist(gmx_ana_nbsearch_t *d, t_pbc *pbc)
+{
+ int maxx, maxy, maxz;
+ int x, y, z, i;
+ real rvnorm;
+
+ /* Find the extent of the sphere in triclinic coordinates */
+ maxz = (int)(d->cutoff * d->recipcell[ZZ][ZZ]) + 1;
+ rvnorm = sqrt(sqr(d->recipcell[YY][YY]) + sqr(d->recipcell[ZZ][YY]));
+ maxy = (int)(d->cutoff * rvnorm) + 1;
+ rvnorm = sqrt(sqr(d->recipcell[XX][XX]) + sqr(d->recipcell[YY][XX])
+ + sqr(d->recipcell[ZZ][XX]));
+ maxx = (int)(d->cutoff * rvnorm) + 1;
+
+ /* Calculate the number of cells and reallocate if necessary */
+ d->ngridnb = (2 * maxx + 1) * (2 * maxy + 1) * (2 * maxz + 1);
+ if (d->gnboffs_nalloc < d->ngridnb)
+ {
+ d->gnboffs_nalloc = d->ngridnb;
+ srenew(d->gnboffs, d->gnboffs_nalloc);
+ }
+
+ /* Store the whole cube */
+ /* TODO: Prune off corners that are not needed */
+ i = 0;
+ for (x = -maxx; x <= maxx; ++x)
+ {
+ for (y = -maxy; y <= maxy; ++y)
+ {
+ for (z = -maxz; z <= maxz; ++z)
+ {
+ d->gnboffs[i][XX] = x;
+ d->gnboffs[i][YY] = y;
+ d->gnboffs[i][ZZ] = z;
+ ++i;
+ }
+ }
+ }
+}
+
+/*! \brief
+ * Determines a suitable grid size.
+ *
+ * \param[in,out] d Grid information.
+ * \param[in] pbc Information about the box.
+ * \returns FALSE if grid search is not suitable.
+ */
+static gmx_bool
+grid_setup_cells(gmx_ana_nbsearch_t *d, t_pbc *pbc)
+{
+ real targetsize;
+ int dd;
+
+#ifdef HAVE_CBRT
+ targetsize = cbrt(pbc->box[XX][XX] * pbc->box[YY][YY] * pbc->box[ZZ][ZZ]
+ * 10 / d->nref);
+#else
+ targetsize = pow(pbc->box[XX][XX] * pbc->box[YY][YY] * pbc->box[ZZ][ZZ]
+ * 10 / d->nref, 1./3.);
+#endif
+
+ d->ncells = 1;
+ for (dd = 0; dd < DIM; ++dd)
+ {
+ d->ncelldim[dd] = (int)(pbc->box[dd][dd] / targetsize);
+ d->ncells *= d->ncelldim[dd];
+ if (d->ncelldim[dd] < 3)
+ {
+ return FALSE;
+ }
+ }
+ /* Reallocate if necessary */
+ if (d->cells_nalloc < d->ncells)
+ {
+ int i;
+
+ srenew(d->ncatoms, d->ncells);
+ srenew(d->catom, d->ncells);
+ srenew(d->catom_nalloc, d->ncells);
+ for (i = d->cells_nalloc; i < d->ncells; ++i)
+ {
+ d->catom[i] = NULL;
+ d->catom_nalloc[i] = 0;
+ }
+ d->cells_nalloc = d->ncells;
+ }
+ return TRUE;
+}
+
+/*! \brief
+ * Sets ua a search grid for a given box.
+ *
+ * \param[in,out] d Grid information.
+ * \param[in] pbc Information about the box.
+ * \returns FALSE if grid search is not suitable.
+ */
+static gmx_bool
+grid_set_box(gmx_ana_nbsearch_t *d, t_pbc *pbc)
+{
+ int dd;
+
+ /* TODO: This check could be improved. */
+ if (0.5*pbc->max_cutoff2 < d->cutoff2)
+ {
+ return FALSE;
+ }
+
+ if (!grid_setup_cells(d, pbc))
+ {
+ return FALSE;
+ }
+
+ d->bTric = TRICLINIC(pbc->box);
+ if (d->bTric)
+ {
+ for (dd = 0; dd < DIM; ++dd)
+ {
+ svmul(1.0 / d->ncelldim[dd], pbc->box[dd], d->cellbox[dd]);
+ }
+ m_inv_ur0(d->cellbox, d->recipcell);
+ }
+ else
+ {
+ for (dd = 0; dd < DIM; ++dd)
+ {
+ d->cellbox[dd][dd] = pbc->box[dd][dd] / d->ncelldim[dd];
+ d->recipcell[dd][dd] = 1 / d->cellbox[dd][dd];
+ }
+ }
+ grid_init_cell_nblist(d, pbc);
+ return TRUE;
+}
+
+/*! \brief
+ * Maps a point into a grid cell.
+ *
+ * \param[in] d Grid information.
+ * \param[in] x Point to map.
+ * \param[out] cell Indices of the grid cell in which \p x lies.
+ *
+ * \p x should be in the triclinic unit cell.
+ */
+static void
+grid_map_onto(gmx_ana_nbsearch_t *d, const rvec x, ivec cell)
+{
+ int dd;
+
+ if (d->bTric)
+ {
+ rvec tx;
+
+ tmvmul_ur0(d->recipcell, x, tx);
+ for (dd = 0; dd < DIM; ++dd)
+ {
+ cell[dd] = (int)tx[dd];
+ }
+ }
+ else
+ {
+ for (dd = 0; dd < DIM; ++dd)
+ {
+ cell[dd] = (int)(x[dd] * d->recipcell[dd][dd]);
+ }
+ }
+}
+
+/*! \brief
+ * Calculates linear index of a grid cell.
+ *
+ * \param[in] d Grid information.
+ * \param[in] cell Cell indices.
+ * \returns Linear index of \p cell.
+ */
+static int
+grid_index(gmx_ana_nbsearch_t *d, const ivec cell)
+{
+ return cell[XX] + cell[YY] * d->ncelldim[XX]
+ + cell[ZZ] * d->ncelldim[XX] * d->ncelldim[YY];
+}
+
+/*! \brief
+ * Clears all grid cells.
+ *
+ * \param[in,out] d Grid information.
+ */
+static void
+grid_clear_cells(gmx_ana_nbsearch_t *d)
+{
+ int ci;
+
+ for (ci = 0; ci < d->ncells; ++ci)
+ {
+ d->ncatoms[ci] = 0;
+ }
+}
+
+/*! \brief
+ * Adds an index into a grid cell.
+ *
+ * \param[in,out] d Grid information.
+ * \param[in] cell Cell into which \p i should be added.
+ * \param[in] i Index to add.
+ */
+static void
+grid_add_to_cell(gmx_ana_nbsearch_t *d, const ivec cell, int i)
+{
+ int ci = grid_index(d, cell);
+
+ if (d->ncatoms[ci] == d->catom_nalloc[ci])
+ {
+ d->catom_nalloc[ci] += 10;
+ srenew(d->catom[ci], d->catom_nalloc[ci]);
+ }
+ d->catom[ci][d->ncatoms[ci]++] = i;
+}
+
+/*!
+ * \param[in,out] d Neighborhood search data structure.
+ * \param[in] pbc PBC information for the frame.
+ * \param[in] n Number of reference positions for the frame.
+ * \param[in] x \p n reference positions for the frame.
+ * \returns 0 on success.
+ *
+ * Initializes the data structure \p d such that it can be used to search
+ * for the neighbors of \p x.
+ */
+int
+gmx_ana_nbsearch_init(gmx_ana_nbsearch_t *d, t_pbc *pbc, int n, rvec x[])
+{
+ d->pbc = pbc;
+ d->nref = n;
+ if (!pbc)
+ {
+ d->bGrid = FALSE;
+ }
+ else if (d->bTryGrid)
+ {
+ d->bGrid = grid_set_box(d, pbc);
+ }
+ if (d->bGrid)
+ {
+ int i;
+
+ if (!d->xref_alloc)
+ {
+ snew(d->xref_alloc, d->maxnref);
+ }
+ d->xref = d->xref_alloc;
+ grid_clear_cells(d);
+
+ for (i = 0; i < n; ++i)
+ {
+ copy_rvec(x[i], d->xref[i]);
+ }
+ put_atoms_in_triclinic_unitcell(ecenterTRIC, pbc->box, n, d->xref);
+ for (i = 0; i < n; ++i)
+ {
+ ivec refcell;
+
+ grid_map_onto(d, d->xref[i], refcell);
+ grid_add_to_cell(d, refcell, i);
+ }
+ }
+ else
+ {
+ d->xref = x;
+ }
+ d->refid = NULL;
+ return 0;
+}
+
+/*!
+ * \param[in,out] d Neighborhood search data structure.
+ * \param[in] pbc PBC information for the frame.
+ * \param[in] p Reference positions for the frame.
+ * \returns 0 on success.
+ *
+ * A convenience wrapper for gmx_ana_nbsearch_init().
+ */
+int
+gmx_ana_nbsearch_pos_init(gmx_ana_nbsearch_t *d, t_pbc *pbc, gmx_ana_pos_t *p)
+{
+ int rc;
+
+ rc = gmx_ana_nbsearch_init(d, pbc, p->nr, p->x);
+ d->refid = (p->nr < d->maxnref ? p->m.refid : NULL);
+ return rc;
+}
+
+/*!
+ * \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.
+ * \returns 0 on success.
+ *
+ * The set exclusions remain in effect until the next call of this function.
+ */
+int
+gmx_ana_nbsearch_set_excl(gmx_ana_nbsearch_t *d, int nexcl, int excl[])
+{
+
+ d->nexcl = nexcl;
+ d->excl = excl;
+ return 0;
+}
+
+/*! \brief
+ * Helper function to check whether a reference point should be excluded.
+ */
+static gmx_bool
+is_excluded(gmx_ana_nbsearch_t *d, int j)
+{
+ if (d->exclind < d->nexcl)
+ {
+ if (d->refid)
+ {
+ while (d->exclind < d->nexcl && d->refid[j] > d->excl[d->exclind])
+ {
+ ++d->exclind;
+ }
+ if (d->exclind < d->nexcl && d->refid[j] == d->excl[d->exclind])
+ {
+ ++d->exclind;
+ return TRUE;
+ }
+ }
+ else
+ {
+ while (d->bGrid && d->exclind < d->nexcl && d->excl[d->exclind] < j)
+ {
+ ++d->exclind;
+ }
+ if (d->excl[d->exclind] == j)
+ {
+ ++d->exclind;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*! \brief
+ * Initializes a grid search to find reference positions neighboring \p x.
+ */
+static void
+grid_search_start(gmx_ana_nbsearch_t *d, rvec x)
+{
+ copy_rvec(x, d->xtest);
+ if (d->bGrid)
+ {
+ put_atoms_in_triclinic_unitcell(ecenterTRIC, d->pbc->box, 1, &d->xtest);
+ grid_map_onto(d, d->xtest, d->testcell);
+ d->prevnbi = 0;
+ d->prevcai = -1;
+ }
+ else
+ {
+ d->previ = -1;
+ }
+ d->exclind = 0;
+}
+
+/*! \brief
+ * Does a grid search.
+ */
+static gmx_bool
+grid_search(gmx_ana_nbsearch_t *d,
+ gmx_bool (*action)(gmx_ana_nbsearch_t *d, int i, real r2))
+{
+ int i;
+ rvec dx;
+ real r2;
+
+ if (d->bGrid)
+ {
+ int nbi, ci, cai;
+
+ nbi = d->prevnbi;
+ cai = d->prevcai + 1;
+
+ for ( ; nbi < d->ngridnb; ++nbi)
+ {
+ ivec cell;
+
+ ivec_add(d->testcell, d->gnboffs[nbi], cell);
+ /* TODO: Support for 2D and screw PBC */
+ cell[XX] = (cell[XX] + d->ncelldim[XX]) % d->ncelldim[XX];
+ cell[YY] = (cell[YY] + d->ncelldim[YY]) % d->ncelldim[YY];
+ cell[ZZ] = (cell[ZZ] + d->ncelldim[ZZ]) % d->ncelldim[ZZ];
+ ci = grid_index(d, cell);
+ /* TODO: Calculate the required PBC shift outside the inner loop */
+ for ( ; cai < d->ncatoms[ci]; ++cai)
+ {
+ i = d->catom[ci][cai];
+ if (is_excluded(d, i))
+ {
+ continue;
+ }
+ pbc_dx_aiuc(d->pbc, d->xtest, d->xref[i], dx);
+ r2 = norm2(dx);
+ if (r2 <= d->cutoff2)
+ {
+ if (action(d, i, r2))
+ {
+ d->prevnbi = nbi;
+ d->prevcai = cai;
+ d->previ = i;
+ return TRUE;
+ }
+ }
+ }
+ d->exclind = 0;
+ cai = 0;
+ }
+ }
+ else
+ {
+ i = d->previ + 1;
+ for ( ; i < d->nref; ++i)
+ {
+ if (is_excluded(d, i))
+ {
+ continue;
+ }
+ if (d->pbc)
+ {
+ pbc_dx(d->pbc, d->xtest, d->xref[i], dx);
+ }
+ else
+ {
+ rvec_sub(d->xtest, d->xref[i], dx);
+ }
+ r2 = norm2(dx);
+ if (r2 <= d->cutoff2)
+ {
+ if (action(d, i, r2))
+ {
+ d->previ = i;
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*! \brief
+ * Helper function to use with grid_search() to find the next neighbor.
+ *
+ * Simply breaks the loop on the first found neighbor.
+ */
+static gmx_bool
+within_action(gmx_ana_nbsearch_t *d, int i, real r2)
+{
+ return TRUE;
+}
+
+/*! \brief
+ * Helper function to use with grid_search() to find the minimum distance.
+ */
+static gmx_bool
+mindist_action(gmx_ana_nbsearch_t *d, int i, real r2)
+{
+ d->cutoff2 = r2;
+ return FALSE;
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[in] x Test position.
+ * \returns TRUE if \p x is within the cutoff of any reference position,
+ * FALSE otherwise.
+ */
+gmx_bool
+gmx_ana_nbsearch_is_within(gmx_ana_nbsearch_t *d, rvec x)
+{
+ grid_search_start(d, x);
+ return grid_search(d, &within_action);
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[in] p Test positions.
+ * \param[in] i Use the i'th position in \p p for testing.
+ * \returns TRUE if the test position is within the cutoff of any reference
+ * position, FALSE otherwise.
+ */
+gmx_bool
+gmx_ana_nbsearch_pos_is_within(gmx_ana_nbsearch_t *d, gmx_ana_pos_t *p, int i)
+{
+ return gmx_ana_nbsearch_is_within(d, p->x[i]);
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[in] x Test position.
+ * \returns The distance to the nearest reference position, or the cutoff
+ * value if there are no reference positions within the cutoff.
+ */
+real
+gmx_ana_nbsearch_mindist(gmx_ana_nbsearch_t *d, rvec x)
+{
+ real mind;
+
+ grid_search_start(d, x);
+ grid_search(d, &mindist_action);
+ mind = sqrt(d->cutoff2);
+ d->cutoff2 = sqr(d->cutoff);
+ return mind;
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[in] p Test positions.
+ * \param[in] i Use the i'th position in \p p for testing.
+ * \returns The distance to the nearest reference position, or the cutoff
+ * value if there are no reference positions within the cutoff.
+ */
+real
+gmx_ana_nbsearch_pos_mindist(gmx_ana_nbsearch_t *d, gmx_ana_pos_t *p, int i)
+{
+ return gmx_ana_nbsearch_mindist(d, p->x[i]);
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[in] x Test positions.
+ * \param[out] jp Index of the reference position in the first pair.
+ * \returns TRUE if there are positions within the cutoff.
+ */
+gmx_bool
+gmx_ana_nbsearch_first_within(gmx_ana_nbsearch_t *d, rvec x, int *jp)
+{
+ grid_search_start(d, x);
+ return gmx_ana_nbsearch_next_within(d, jp);
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[in] p Test positions.
+ * \param[in] i Use the i'th position in \p p.
+ * \param[out] jp Index of the reference position in the first pair.
+ * \returns TRUE if there are positions within the cutoff.
+ */
+gmx_bool
+gmx_ana_nbsearch_pos_first_within(gmx_ana_nbsearch_t *d, gmx_ana_pos_t *p,
+ int i, int *jp)
+{
+ return gmx_ana_nbsearch_first_within(d, p->x[i], jp);
+}
+
+/*!
+ * \param[in] d Neighborhood search data structure.
+ * \param[out] jp Index of the test position in the next pair.
+ * \returns TRUE if there are positions within the cutoff.
+ */
+gmx_bool
+gmx_ana_nbsearch_next_within(gmx_ana_nbsearch_t *d, int *jp)
+{
+ if (grid_search(d, &within_action))
+ {
+ *jp = d->previ;
+ return TRUE;
+ }
+ *jp = -1;
+ return FALSE;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief API for neighborhood searching.
+ *
+ * The API is documented in more detail on a separate page:
+ * \ref nbsearch
+ *
+ * The functions within this file can be used independently of the other parts
+ * of the library.
+ * The library also uses the functions internally.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_NBSEARCH_H
+#define GMX_SELECTION_NBSEARCH_H
+
+#include "../legacyheaders/typedefs.h"
+
+#include "indexutil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct gmx_ana_pos_t;
+
+/** Data structure for neighborhood searches. */
+typedef struct gmx_ana_nbsearch_t gmx_ana_nbsearch_t;
+
+/** Create a new neighborhood search data structure. */
+int
+gmx_ana_nbsearch_create(gmx_ana_nbsearch_t **d, real cutoff, int maxn);
+/** Free memory allocated for neighborhood search. */
+void
+gmx_ana_nbsearch_free(gmx_ana_nbsearch_t *d);
+
+/** Initializes neighborhood search for a new frame. */
+int
+gmx_ana_nbsearch_init(gmx_ana_nbsearch_t *d, t_pbc *pbc, int n, rvec x[]);
+/** Initializes neighborhood search for a frame using \c gmx_ana_pos_t. */
+int
+gmx_ana_nbsearch_pos_init(gmx_ana_nbsearch_t *d, t_pbc *pbc,
+ struct gmx_ana_pos_t *p);
+/** Sets the exclusions for the next neighborhood search. */
+int
+gmx_ana_nbsearch_set_excl(gmx_ana_nbsearch_t *d, int nexcl, int excl[]);
+/** Check whether a point is within a neighborhood. */
+gmx_bool
+gmx_ana_nbsearch_is_within(gmx_ana_nbsearch_t *d, rvec x);
+/** Check whether a position is within a neighborhood. */
+gmx_bool
+gmx_ana_nbsearch_pos_is_within(gmx_ana_nbsearch_t *d,
+ struct gmx_ana_pos_t *p, int i);
+/** Calculates the minimun distance from the reference points. */
+real
+gmx_ana_nbsearch_mindist(gmx_ana_nbsearch_t *d, rvec x);
+/** Calculates the minimun distance from the reference points. */
+real
+gmx_ana_nbsearch_pos_mindist(gmx_ana_nbsearch_t *d,
+ struct gmx_ana_pos_t *p, int i);
+/** Finds the first reference position within the cutoff. */
+gmx_bool
+gmx_ana_nbsearch_first_within(gmx_ana_nbsearch_t *d, rvec x, int *jp);
+/** Finds the first reference position within the cutoff. */
+gmx_bool
+gmx_ana_nbsearch_pos_first_within(gmx_ana_nbsearch_t *d,
+ struct gmx_ana_pos_t *p, int i, int *jp);
+/** Finds the next reference position within the cutoff. */
+gmx_bool
+gmx_ana_nbsearch_next_within(gmx_ana_nbsearch_t *d, int *jp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in selparam.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <smalloc.h>
+#include <string2.h>
+#include <vec.h>
+
+#include "gromacs/errorreporting/errorcontext.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+#include "gromacs/selection/selparam.h"
+
+#include "parsetree.h"
+#include "position.h"
+#include "scanner.h"
+#include "selelem.h"
+
+template <typename T>
+static T min(T a, T b)
+{
+ return (a < b) ? a : b;
+}
+
+template <typename T>
+static T max(T a, T b)
+{
+ return (a > b) ? a : b;
+}
+
+/*!
+ * \param[in] name Name of the parameter to search.
+ * \param[in] nparam Number of parameters in the \p param array.
+ * \param[in] param Parameter array to search.
+ * \returns Pointer to the parameter in the \p param
+ * or NULL if no parameter with name \p name was found.
+ *
+ * The comparison is case-sensitive.
+ */
+gmx_ana_selparam_t *
+gmx_ana_selparam_find(const char *name, int nparam, gmx_ana_selparam_t *param)
+{
+ int i;
+
+ if (nparam == 0)
+ {
+ return NULL;
+ }
+ /* Find the first non-null parameter */
+ i = 0;
+ while (i < nparam && param[i].name == NULL)
+ {
+ ++i;
+ }
+ /* Process the special case of a NULL parameter */
+ if (name == NULL)
+ {
+ return (i == 0) ? NULL : ¶m[i-1];
+ }
+ for ( ; i < nparam; ++i)
+ {
+ if (!strcmp(param[i].name, name))
+ {
+ return ¶m[i];
+ }
+ /* Check for 'no' prefix on gmx_boolean parameters */
+ if (param[i].val.type == NO_VALUE
+ && strlen(name) > 2 && name[0] == 'n' && name[1] == 'o'
+ && !strcmp(param[i].name, name+2))
+ {
+ return ¶m[i];
+ }
+ }
+ return NULL;
+}
+
+/*! \brief
+ * Does a type conversion on a \c t_selexpr_value.
+ *
+ * \param[in,out] value Value to convert.
+ * \param[in] type Type to convert to.
+ * \param[in] scanner Scanner data structure.
+ * \returns 0 on success, a non-zero value on error.
+ */
+static int
+convert_value(t_selexpr_value *value, e_selvalue_t type, void *scanner)
+{
+ if (value->type == type || type == NO_VALUE)
+ {
+ return 0;
+ }
+ if (value->bExpr)
+ {
+ /* Conversion from atom selection to position using default
+ * reference positions. */
+ if (value->type == GROUP_VALUE && type == POS_VALUE)
+ {
+ value->u.expr =
+ _gmx_sel_init_position(value->u.expr, NULL, scanner);
+ if (value->u.expr == NULL)
+ {
+ return -1;
+ }
+ value->type = type;
+ return 0;
+ }
+ return -1;
+ }
+ else
+ {
+ /* Integers to floating point are easy */
+ if (value->type == INT_VALUE && type == REAL_VALUE)
+ {
+ value->u.r.r1 = (real)value->u.i.i1;
+ value->u.r.r2 = (real)value->u.i.i2;
+ value->type = type;
+ return 0;
+ }
+ /* Reals that are integer-valued can also be converted */
+ if (value->type == REAL_VALUE && type == INT_VALUE
+ && gmx_within_tol(value->u.r.r1, (int)value->u.r.r1, GMX_REAL_EPS)
+ && gmx_within_tol(value->u.r.r2, (int)value->u.r.r2, GMX_REAL_EPS))
+ {
+ value->u.i.i1 = (int)value->u.r.r1;
+ value->u.i.i2 = (int)value->u.r.r2;
+ value->type = type;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*! \brief
+ * Does a type conversion on a list of values.
+ *
+ * \param[in,out] values Values to convert.
+ * \param[in] type Type to convert to.
+ * \param[in] scanner Scanner data structure.
+ * \returns 0 on success, a non-zero value on error.
+ */
+static int
+convert_values(t_selexpr_value *values, e_selvalue_t type, void *scanner)
+{
+ t_selexpr_value *value;
+ int rc, rc1;
+
+ rc = 0;
+ value = values;
+ while (value)
+ {
+ rc1 = convert_value(value, type, scanner);
+ if (rc1 != 0 && rc == 0)
+ {
+ rc = rc1;
+ }
+ value = value->next;
+ }
+ /* FIXME: More informative error messages */
+ return rc;
+}
+
+/*! \brief
+ * Adds a child element for a parameter, keeping the parameter order.
+ *
+ * \param[in,out] root Root element to which the child is added.
+ * \param[in] child Child to add.
+ * \param[in] param Parameter for which this child is a value.
+ *
+ * Puts \p child in the child list of \p root such that the list remains
+ * in the same order as the corresponding parameters.
+ */
+static void
+place_child(t_selelem *root, t_selelem *child, gmx_ana_selparam_t *param)
+{
+ gmx_ana_selparam_t *ps;
+ int n;
+
+ ps = root->u.expr.method->param;
+ n = param - ps;
+ /* Put the child element in the correct place */
+ if (!root->child || n < root->child->u.param - ps)
+ {
+ child->next = root->child;
+ root->child = child;
+ }
+ else
+ {
+ t_selelem *prev;
+
+ prev = root->child;
+ while (prev->next && prev->next->u.param - ps >= n)
+ {
+ prev = prev->next;
+ }
+ child->next = prev->next;
+ prev->next = child;
+ }
+}
+
+/*! \brief
+ * Comparison function for sorting integer ranges.
+ *
+ * \param[in] a Pointer to the first range.
+ * \param[in] b Pointer to the second range.
+ * \returns -1, 0, or 1 depending on the relative order of \p a and \p b.
+ *
+ * The ranges are primarily sorted based on their starting point, and
+ * secondarily based on length (longer ranges come first).
+ */
+static int
+cmp_int_range(const void *a, const void *b)
+{
+ if (((int *)a)[0] < ((int *)b)[0])
+ {
+ return -1;
+ }
+ if (((int *)a)[0] > ((int *)b)[0])
+ {
+ return 1;
+ }
+ if (((int *)a)[1] > ((int *)b)[1])
+ {
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief
+ * Comparison function for sorting real ranges.
+ *
+ * \param[in] a Pointer to the first range.
+ * \param[in] b Pointer to the second range.
+ * \returns -1, 0, or 1 depending on the relative order of \p a and \p b.
+ *
+ * The ranges are primarily sorted based on their starting point, and
+ * secondarily based on length (longer ranges come first).
+ */
+static int
+cmp_real_range(const void *a, const void *b)
+{
+ if (((real *)a)[0] < ((real *)b)[0])
+ {
+ return -1;
+ }
+ if (((real *)a)[0] > ((real *)b)[0])
+ {
+ return 1;
+ }
+ if (((real *)a)[1] > ((real *)b)[1])
+ {
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief
+ * Parses the values for a parameter that takes integer or real ranges.
+ *
+ * \param[in] nval Number of values in \p values.
+ * \param[in] values Pointer to the list of values.
+ * \param param Parameter to parse.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the values were parsed successfully, FALSE otherwise.
+ */
+static gmx_bool
+parse_values_range(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param,
+ void *scanner)
+{
+ t_selexpr_value *value;
+ int *idata;
+ real *rdata;
+ int i, j, n;
+
+ param->flags &= ~SPAR_DYNAMIC;
+ if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid range parameter type");
+ return FALSE;
+ }
+ idata = NULL;
+ rdata = NULL;
+ if (param->val.type == INT_VALUE)
+ {
+ snew(idata, nval*2);
+ }
+ else
+ {
+ snew(rdata, nval*2);
+ }
+ value = values;
+ i = 0;
+ while (value)
+ {
+ if (value->bExpr)
+ {
+ _gmx_selparser_error(scanner, "expressions not supported within range parameters");
+ return FALSE;
+ }
+ if (value->type != param->val.type)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid range value type");
+ return FALSE;
+ }
+ if (param->val.type == INT_VALUE)
+ {
+ /* Make sure the input range is in increasing order */
+ if (value->u.i.i1 > value->u.i.i2)
+ {
+ int tmp = value->u.i.i1;
+ value->u.i.i1 = value->u.i.i2;
+ value->u.i.i2 = tmp;
+ }
+ /* Check if the new range overlaps or extends the previous one */
+ if (i > 0 && value->u.i.i1 <= idata[i-1]+1 && value->u.i.i2 >= idata[i-2]-1)
+ {
+ idata[i-2] = min(idata[i-2], value->u.i.i1);
+ idata[i-1] = max(idata[i-1], value->u.i.i2);
+ }
+ else
+ {
+ idata[i++] = value->u.i.i1;
+ idata[i++] = value->u.i.i2;
+ }
+ }
+ else
+ {
+ /* Make sure the input range is in increasing order */
+ if (value->u.r.r1 > value->u.r.r2)
+ {
+ real tmp = value->u.r.r1;
+ value->u.r.r1 = value->u.r.r2;
+ value->u.r.r2 = tmp;
+ }
+ /* Check if the new range overlaps or extends the previous one */
+ if (i > 0 && value->u.r.r1 <= rdata[i-1] && value->u.r.r2 >= rdata[i-2])
+ {
+ rdata[i-2] = min(rdata[i-2], value->u.r.r1);
+ rdata[i-1] = max(rdata[i-1], value->u.r.r2);
+ }
+ else
+ {
+ rdata[i++] = value->u.r.r1;
+ rdata[i++] = value->u.r.r2;
+ }
+ }
+ value = value->next;
+ }
+ n = i/2;
+ /* Sort the ranges and merge consequent ones */
+ if (param->val.type == INT_VALUE)
+ {
+ qsort(idata, n, 2*sizeof(int), &cmp_int_range);
+ for (i = j = 2; i < 2*n; i += 2)
+ {
+ if (idata[j-1]+1 >= idata[i])
+ {
+ if (idata[i+1] > idata[j-1])
+ {
+ idata[j-1] = idata[i+1];
+ }
+ }
+ else
+ {
+ idata[j] = idata[i];
+ idata[j+1] = idata[i+1];
+ j += 2;
+ }
+ }
+ }
+ else
+ {
+ qsort(rdata, n, 2*sizeof(real), &cmp_real_range);
+ for (i = j = 2; i < 2*n; i += 2)
+ {
+ if (rdata[j-1]+1 >= rdata[i])
+ {
+ if (rdata[i+1] > rdata[j-1])
+ {
+ rdata[j-1] = rdata[i+1];
+ }
+ }
+ else
+ {
+ rdata[j] = rdata[i];
+ rdata[j+1] = rdata[i+1];
+ j += 2;
+ }
+ }
+ }
+ n = j/2;
+ /* Store the values */
+ if (param->flags & SPAR_VARNUM)
+ {
+ param->val.nr = n;
+ if (param->val.type == INT_VALUE)
+ {
+ srenew(idata, j);
+ _gmx_selvalue_setstore_alloc(¶m->val, idata, j);
+ }
+ else
+ {
+ srenew(rdata, j);
+ _gmx_selvalue_setstore_alloc(¶m->val, rdata, j);
+ }
+ }
+ else
+ {
+ if (n != param->val.nr)
+ {
+ _gmx_selparser_error(scanner, "the value should consist of exactly one range");
+ sfree(idata);
+ sfree(rdata);
+ return FALSE;
+ }
+ if (param->val.type == INT_VALUE)
+ {
+ memcpy(param->val.u.i, idata, 2*n*sizeof(int));
+ sfree(idata);
+ }
+ else
+ {
+ memcpy(param->val.u.r, rdata, 2*n*sizeof(real));
+ sfree(rdata);
+ }
+ }
+ if (param->nvalptr)
+ {
+ *param->nvalptr = param->val.nr;
+ }
+ param->nvalptr = NULL;
+
+ return TRUE;
+}
+
+/*! \brief
+ * Parses the values for a parameter that takes a variable number of values.
+ *
+ * \param[in] nval Number of values in \p values.
+ * \param[in] values Pointer to the list of values.
+ * \param param Parameter to parse.
+ * \param root Selection element to which child expressions are added.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the values were parsed successfully, FALSE otherwise.
+ *
+ * For integer ranges, the sequence of numbers from the first to second value
+ * is stored, each as a separate value.
+ */
+static gmx_bool
+parse_values_varnum(int nval, t_selexpr_value *values,
+ gmx_ana_selparam_t *param, t_selelem *root, void *scanner)
+{
+ t_selexpr_value *value;
+ int i, j;
+
+ param->flags &= ~SPAR_DYNAMIC;
+ /* Update nval if there are integer ranges. */
+ if (param->val.type == INT_VALUE)
+ {
+ value = values;
+ while (value)
+ {
+ if (value->type == INT_VALUE && !value->bExpr)
+ {
+ nval += abs(value->u.i.i2 - value->u.i.i1);
+ }
+ value = value->next;
+ }
+ }
+
+ /* Check that the value type is actually implemented */
+ if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE
+ && param->val.type != STR_VALUE && param->val.type != POS_VALUE)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Variable-count value type not implemented");
+ return FALSE;
+ }
+
+ /* Reserve appropriate amount of memory */
+ if (param->val.type == POS_VALUE)
+ {
+ gmx_ana_pos_reserve(param->val.u.p, nval, 0);
+ gmx_ana_pos_set_nr(param->val.u.p, nval);
+ gmx_ana_indexmap_init(¶m->val.u.p->m, NULL, NULL, INDEX_UNKNOWN);
+ }
+ else
+ {
+ _gmx_selvalue_reserve(¶m->val, nval);
+ }
+
+ value = values;
+ i = 0;
+ while (value)
+ {
+ if (value->bExpr)
+ {
+ _gmx_selparser_error(scanner, "expressions not supported within value lists");
+ return FALSE;
+ }
+ if (value->type != param->val.type)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid value type");
+ return FALSE;
+ }
+ switch (param->val.type)
+ {
+ case INT_VALUE:
+ if (value->u.i.i1 <= value->u.i.i2)
+ {
+ for (j = value->u.i.i1; j <= value->u.i.i2; ++j)
+ {
+ param->val.u.i[i++] = j;
+ }
+ }
+ else
+ {
+ for (j = value->u.i.i1; j >= value->u.i.i2; --j)
+ {
+ param->val.u.i[i++] = j;
+ }
+ }
+ break;
+ case REAL_VALUE:
+ if (value->u.r.r1 != value->u.r.r2)
+ {
+ _gmx_selparser_error(scanner, "real ranges not supported");
+ return FALSE;
+ }
+ param->val.u.r[i++] = value->u.r.r1;
+ break;
+ case STR_VALUE: param->val.u.s[i++] = strdup(value->u.s); break;
+ case POS_VALUE: copy_rvec(value->u.x, param->val.u.p->x[i++]); break;
+ default: /* Should not be reached */
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid value type");
+ return FALSE;
+ }
+ value = value->next;
+ }
+ param->val.nr = i;
+ if (param->nvalptr)
+ {
+ *param->nvalptr = param->val.nr;
+ }
+ param->nvalptr = NULL;
+ /* Create a dummy child element to store the string values.
+ * This element is responsible for freeing the values, but carries no
+ * other function. */
+ if (param->val.type == STR_VALUE)
+ {
+ t_selelem *child;
+
+ child = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype(child, STR_VALUE);
+ child->name = param->name;
+ child->flags &= ~SEL_ALLOCVAL;
+ child->flags |= SEL_FLAGSSET | SEL_VARNUMVAL | SEL_ALLOCDATA;
+ child->v.nr = param->val.nr;
+ _gmx_selvalue_setstore(&child->v, param->val.u.s);
+ /* Because the child is not group-valued, the u union is not used
+ * for anything, so we can abuse it by storing the parameter value
+ * as place_child() expects, but this is really ugly... */
+ child->u.param = param;
+ place_child(root, child, param);
+ }
+
+ return TRUE;
+}
+
+/*! \brief
+ * Adds a new subexpression reference to a selection element.
+ *
+ * \param[in,out] root Root element to which the subexpression is added.
+ * \param[in] param Parameter for which this expression is a value.
+ * \param[in] expr Expression to add.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created child element.
+ *
+ * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child
+ * list of \p root.
+ * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is.
+ * \ref SEL_ALLOCVAL is cleared for the returned element.
+ */
+static t_selelem *
+add_child(t_selelem *root, gmx_ana_selparam_t *param, t_selelem *expr,
+ void *scanner)
+{
+ t_selelem *child;
+ int rc;
+
+ if (root->type != SEL_EXPRESSION && root->type != SEL_MODIFIER)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Unsupported root element for selection parameter parser");
+ return NULL;
+ }
+ /* Create a subexpression reference element if necessary */
+ if (expr->type == SEL_SUBEXPRREF)
+ {
+ child = expr;
+ }
+ else
+ {
+ child = _gmx_selelem_create(SEL_SUBEXPRREF);
+ if (!child)
+ {
+ return NULL;
+ }
+ _gmx_selelem_set_vtype(child, expr->v.type);
+ child->child = expr;
+ }
+ /* Setup the child element */
+ child->flags &= ~SEL_ALLOCVAL;
+ child->u.param = param;
+ if (child->v.type != param->val.type)
+ {
+ _gmx_selparser_error(scanner, "invalid expression value");
+ goto on_error;
+ }
+ rc = _gmx_selelem_update_flags(child, scanner);
+ if (rc != 0)
+ {
+ goto on_error;
+ }
+ if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC))
+ {
+ _gmx_selparser_error(scanner, "dynamic values not supported");
+ goto on_error;
+ }
+ if (!(child->flags & SEL_DYNAMIC))
+ {
+ param->flags &= ~SPAR_DYNAMIC;
+ }
+ /* Put the child element in the correct place */
+ place_child(root, child, param);
+ return child;
+
+on_error:
+ if (child != expr)
+ {
+ _gmx_selelem_free(child);
+ }
+ return NULL;
+}
+
+/*! \brief
+ * Parses an expression value for a parameter that takes a variable number of values.
+ *
+ * \param[in] nval Number of values in \p values.
+ * \param[in] values Pointer to the list of values.
+ * \param param Parameter to parse.
+ * \param root Selection element to which child expressions are added.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the values were parsed successfully, FALSE otherwise.
+ */
+static gmx_bool
+parse_values_varnum_expr(int nval, t_selexpr_value *values,
+ gmx_ana_selparam_t *param, t_selelem *root,
+ void *scanner)
+{
+ t_selexpr_value *value;
+ t_selelem *child;
+ t_selelem *expr;
+
+ if (nval != 1 || !values->bExpr)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid expression value");
+ return FALSE;
+ }
+
+ value = values;
+ child = add_child(root, param, value->u.expr, scanner);
+ value->u.expr = NULL;
+ if (!child)
+ {
+ return FALSE;
+ }
+
+ /* Process single-valued expressions */
+ /* TODO: We should also handle SEL_SINGLEVAL expressions here */
+ if (child->v.type == POS_VALUE || child->v.type == GROUP_VALUE)
+ {
+ /* Set the value storage */
+ _gmx_selvalue_setstore(&child->v, param->val.u.ptr);
+ param->val.nr = 1;
+ if (param->nvalptr)
+ {
+ *param->nvalptr = param->val.nr;
+ }
+ param->nvalptr = NULL;
+ return TRUE;
+ }
+
+ if (!(child->flags & SEL_VARNUMVAL))
+ {
+ _gmx_selparser_error(scanner, "invalid expression value");
+ return FALSE;
+ }
+
+ child->flags |= SEL_ALLOCVAL;
+ param->val.nr = -1;
+ *param->nvalptr = param->val.nr;
+ /* Rest of the initialization is done during compilation in
+ * init_method(). */
+
+ return TRUE;
+}
+
+/*! \brief
+ * Initializes the storage of an expression value.
+ *
+ * \param[in,out] sel Selection element that evaluates the value.
+ * \param[in] param Parameter to receive the value.
+ * \param[in] i The value of \p sel evaluates the value \p i for
+ * \p param.
+ * \param[in] scanner Scanner data structure.
+ *
+ * Initializes the data pointer of \p sel such that the result is stored
+ * as the value \p i of \p param.
+ * This function is used internally by parse_values_std().
+ */
+static gmx_bool
+set_expr_value_store(t_selelem *sel, gmx_ana_selparam_t *param, int i,
+ void *scanner)
+{
+ if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL))
+ {
+ _gmx_selparser_error(scanner, "invalid expression value");
+ return FALSE;
+ }
+ switch (sel->v.type)
+ {
+ case INT_VALUE: sel->v.u.i = ¶m->val.u.i[i]; break;
+ case REAL_VALUE: sel->v.u.r = ¶m->val.u.r[i]; break;
+ case STR_VALUE: sel->v.u.s = ¶m->val.u.s[i]; break;
+ case POS_VALUE: sel->v.u.p = ¶m->val.u.p[i]; break;
+ case GROUP_VALUE: sel->v.u.g = ¶m->val.u.g[i]; break;
+ default: /* Error */
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid value type");
+ return FALSE;
+ }
+ sel->v.nr = 1;
+ sel->v.nalloc = -1;
+ return TRUE;
+}
+
+/*! \brief
+ * Parses the values for a parameter that takes a constant number of values.
+ *
+ * \param[in] nval Number of values in \p values.
+ * \param[in] values Pointer to the list of values.
+ * \param param Parameter to parse.
+ * \param root Selection element to which child expressions are added.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the values were parsed successfully, FALSE otherwise.
+ *
+ * For integer ranges, the sequence of numbers from the first to second value
+ * is stored, each as a separate value.
+ */
+static gmx_bool
+parse_values_std(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param,
+ t_selelem *root, void *scanner)
+{
+ t_selexpr_value *value;
+ t_selelem *child;
+ int i, j;
+ gmx_bool bDynamic;
+
+ /* Handle atom-valued parameters */
+ if (param->flags & SPAR_ATOMVAL)
+ {
+ if (nval > 1)
+ {
+ _gmx_selparser_error(scanner, "more than one value not supported");
+ return FALSE;
+ }
+ value = values;
+ if (value->bExpr)
+ {
+ child = add_child(root, param, value->u.expr, scanner);
+ value->u.expr = NULL;
+ if (!child)
+ {
+ return FALSE;
+ }
+ child->flags |= SEL_ALLOCVAL;
+ if (child->v.type != GROUP_VALUE && (child->flags & SEL_ATOMVAL))
+ {
+ /* Rest of the initialization is done during compilation in
+ * init_method(). */
+ /* TODO: Positions are not correctly handled */
+ param->val.nr = -1;
+ if (param->nvalptr)
+ {
+ *param->nvalptr = -1;
+ }
+ return TRUE;
+ }
+ param->flags &= ~SPAR_ATOMVAL;
+ param->val.nr = 1;
+ if (param->nvalptr)
+ {
+ *param->nvalptr = 1;
+ }
+ param->nvalptr = NULL;
+ if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
+ || param->val.type == STR_VALUE)
+ {
+ _gmx_selvalue_reserve(¶m->val, 1);
+ }
+ return set_expr_value_store(child, param, 0, scanner);
+ }
+ /* If we reach here, proceed with normal parameter handling */
+ param->val.nr = 1;
+ if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
+ || param->val.type == STR_VALUE)
+ {
+ _gmx_selvalue_reserve(¶m->val, 1);
+ }
+ param->flags &= ~SPAR_ATOMVAL;
+ param->flags &= ~SPAR_DYNAMIC;
+ }
+
+ value = values;
+ i = 0;
+ bDynamic = FALSE;
+ while (value && i < param->val.nr)
+ {
+ if (value->type != param->val.type)
+ {
+ _gmx_selparser_warning(scanner, "incorrect value skipped");
+ value = value->next;
+ continue;
+ }
+ if (value->bExpr)
+ {
+ child = add_child(root, param, value->u.expr, scanner);
+ /* Clear the expression from the value once it is stored */
+ value->u.expr = NULL;
+ /* Check that the expression is valid */
+ if (!child)
+ {
+ return FALSE;
+ }
+ if (!set_expr_value_store(child, param, i, scanner))
+ {
+ return FALSE;
+ }
+ if (child->flags & SEL_DYNAMIC)
+ {
+ bDynamic = TRUE;
+ }
+ }
+ else
+ {
+ /* Value is not an expression */
+ switch (value->type)
+ {
+ case INT_VALUE:
+ if (value->u.i.i1 <= value->u.i.i2)
+ {
+ for (j = value->u.i.i1; j <= value->u.i.i2 && i < param->val.nr; ++j)
+ {
+ param->val.u.i[i++] = j;
+ }
+ if (j != value->u.i.i2 + 1)
+ {
+ _gmx_selparser_warning(scanner, "extra values skipped");
+ }
+ }
+ else
+ {
+ for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j)
+ {
+ param->val.u.i[i++] = j;
+ }
+ if (j != value->u.i.i2 - 1)
+ {
+ _gmx_selparser_warning(scanner, "extra values skipped");
+ }
+ }
+ --i;
+ break;
+ case REAL_VALUE:
+ if (value->u.r.r1 != value->u.r.r2)
+ {
+ _gmx_selparser_error(scanner, "real ranges not supported");
+ return FALSE;
+ }
+ param->val.u.r[i] = value->u.r.r1;
+ break;
+ case STR_VALUE:
+ param->val.u.s[i] = strdup(value->u.s);
+ break;
+ case POS_VALUE:
+ gmx_ana_pos_init_const(¶m->val.u.p[i], value->u.x);
+ break;
+ case NO_VALUE:
+ case GROUP_VALUE:
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Invalid non-expression value");
+ return FALSE;
+ }
+ }
+ ++i;
+ value = value->next;
+ }
+ if (value != NULL)
+ {
+ _gmx_selparser_error(scanner, "extra values'");
+ return FALSE;
+ }
+ if (i < param->val.nr)
+ {
+ _gmx_selparser_error(scanner, "not enough values");
+ return FALSE;
+ }
+ if (!bDynamic)
+ {
+ param->flags &= ~SPAR_DYNAMIC;
+ }
+ if (param->nvalptr)
+ {
+ *param->nvalptr = param->val.nr;
+ }
+ param->nvalptr = NULL;
+
+ return TRUE;
+}
+
+/*! \brief
+ * Parses the values for a boolean parameter.
+ *
+ * \param[in] name Name by which the parameter was given.
+ * \param[in] nval Number of values in \p values.
+ * \param[in] values Pointer to the list of values.
+ * \param param Parameter to parse.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the values were parsed successfully, FALSE otherwise.
+ */
+static gmx_bool
+parse_values_bool(const char *name, int nval, t_selexpr_value *values,
+ gmx_ana_selparam_t *param, void *scanner)
+{
+ gmx_bool bSetNo;
+ int len;
+
+ if (param->val.type != NO_VALUE)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid boolean parameter");
+ return FALSE;
+ }
+ if (nval > 1 || (values && values->type != INT_VALUE))
+ {
+ _gmx_selparser_error(scanner, "parameter takes only a yes/no/on/off/0/1 value");
+ return FALSE;
+ }
+
+ bSetNo = FALSE;
+ /* Check if the parameter name is given with a 'no' prefix */
+ len = strlen(name);
+ if (len > 2 && name[0] == 'n' && name[1] == 'o'
+ && strncmp(name+2, param->name, len-2) == 0)
+ {
+ bSetNo = TRUE;
+ }
+ if (bSetNo && nval > 0)
+ {
+ _gmx_selparser_error(scanner, "parameter 'no%s' should not have a value",
+ param->name);
+ return FALSE;
+ }
+ if (values && values->u.i.i1 == 0)
+ {
+ bSetNo = TRUE;
+ }
+
+ *param->val.u.b = bSetNo ? FALSE : TRUE;
+ return TRUE;
+}
+
+/*! \brief
+ * Parses the values for an enumeration parameter.
+ *
+ * \param[in] nval Number of values in \p values.
+ * \param[in] values Pointer to the list of values.
+ * \param param Parameter to parse.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the values were parsed successfully, FALSE otherwise.
+ */
+static gmx_bool
+parse_values_enum(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param,
+ void *scanner)
+{
+ int i, len, match;
+
+ if (nval != 1)
+ {
+ _gmx_selparser_error(scanner, "a single value is required");
+ return FALSE;
+ }
+ if (values->type != STR_VALUE || param->val.type != STR_VALUE)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError, "Invalid enum parameter");
+ return FALSE;
+ }
+ if (values->bExpr)
+ {
+ _gmx_selparser_error(scanner, "expression value for enumerated parameter not supported");
+ return FALSE;
+ }
+
+ len = strlen(values->u.s);
+ i = 1;
+ match = 0;
+ while (param->val.u.s[i] != NULL)
+ {
+ if (strncmp(values->u.s, param->val.u.s[i], len) == 0)
+ {
+ /* Check if there is a duplicate match */
+ if (match > 0)
+ {
+ _gmx_selparser_error(scanner, "ambiguous value");
+ return FALSE;
+ }
+ match = i;
+ }
+ ++i;
+ }
+ if (match == 0)
+ {
+ _gmx_selparser_error(scanner, "invalid value");
+ return FALSE;
+ }
+ param->val.u.s[0] = param->val.u.s[match];
+ return TRUE;
+}
+
+/*! \brief
+ * Replaces constant expressions with their values.
+ *
+ * \param[in,out] values First element in the value list to process.
+ */
+static void
+convert_const_values(t_selexpr_value *values)
+{
+ t_selexpr_value *val;
+
+ val = values;
+ while (val)
+ {
+ if (val->bExpr && val->u.expr->v.type != GROUP_VALUE &&
+ val->u.expr->type == SEL_CONST)
+ {
+ t_selelem *expr = val->u.expr;
+ val->bExpr = FALSE;
+ switch (expr->v.type)
+ {
+ case INT_VALUE:
+ val->u.i.i1 = val->u.i.i2 = expr->v.u.i[0];
+ break;
+ case REAL_VALUE:
+ val->u.r.r1 = val->u.r.r2 = expr->v.u.r[0];
+ break;
+ case STR_VALUE:
+ val->u.s = expr->v.u.s[0];
+ break;
+ case POS_VALUE:
+ copy_rvec(expr->v.u.p->x[0], val->u.x);
+ break;
+ default:
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Unsupported value type");
+ break;
+ }
+ _gmx_selelem_free(expr);
+ }
+ val = val->next;
+ }
+}
+
+/*!
+ * \param pparams List of parameters from the selection parser.
+ * \param[in] nparam Number of parameters in \p params.
+ * \param params Array of parameters to parse.
+ * \param root Selection element to which child expressions are added.
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the parameters were parsed successfully, FALSE otherwise.
+ *
+ * Initializes the \p params array based on the parameters in \p pparams.
+ * See the documentation of \c gmx_ana_selparam_t for different options
+ * available for parsing.
+ *
+ * The list \p pparams and any associated values are freed after the parameters
+ * have been processed, no matter is there was an error or not.
+ */
+gmx_bool
+_gmx_sel_parse_params(t_selexpr_param *pparams, int nparam, gmx_ana_selparam_t *params,
+ t_selelem *root, void *scanner)
+{
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ t_selexpr_param *pparam;
+ gmx_ana_selparam_t *oparam;
+ gmx_bool bOk, rc;
+ int i;
+
+ /* Check that the value pointers of SPAR_VARNUM parameters are NULL and
+ * that they are not NULL for other parameters */
+ bOk = TRUE;
+ for (i = 0; i < nparam; ++i)
+ {
+ char buf[128];
+ sprintf(buf, "In parameter '%s'", params[i].name);
+ gmx::ErrorContext context(errors, buf);
+ if (params[i].val.type != POS_VALUE && (params[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
+ {
+ if (params[i].val.u.ptr != NULL)
+ {
+ _gmx_selparser_warning(scanner, "value pointer is not NULL "
+ "although it should be for SPAR_VARNUM "
+ "and SPAR_ATOMVAL parameters");
+ }
+ if ((params[i].flags & SPAR_VARNUM)
+ && (params[i].flags & SPAR_DYNAMIC) && !params[i].nvalptr)
+ {
+ _gmx_selparser_error(scanner, "nvalptr is NULL but both "
+ "SPAR_VARNUM and SPAR_DYNAMIC are specified");
+ bOk = FALSE;
+ }
+ }
+ else
+ {
+ if (params[i].val.u.ptr == NULL)
+ {
+ _gmx_selparser_error(scanner, "value pointer is NULL");
+ bOk = FALSE;
+ }
+ }
+ }
+ if (!bOk)
+ {
+ _gmx_selexpr_free_params(pparams);
+ return FALSE;
+ }
+ /* Parse the parameters */
+ pparam = pparams;
+ i = 0;
+ while (pparam)
+ {
+ char buf[128];
+ /* Find the parameter and make some checks */
+ if (pparam->name != NULL)
+ {
+ sprintf(buf, "In parameter '%s'", pparam->name);
+ i = -1;
+ oparam = gmx_ana_selparam_find(pparam->name, nparam, params);
+ }
+ else if (i >= 0)
+ {
+ sprintf(buf, "In value %d", i + 1);
+ oparam = ¶ms[i];
+ if (oparam->name != NULL)
+ {
+ oparam = NULL;
+ _gmx_selparser_error(scanner, "too many NULL parameters provided");
+ bOk = FALSE;
+ pparam = pparam->next;
+ continue;
+ }
+ ++i;
+ }
+ else
+ {
+ _gmx_selparser_error(scanner, "all NULL parameters should appear in the beginning of the list");
+ bOk = FALSE;
+ pparam = pparam->next;
+ continue;
+ }
+ gmx::ErrorContext context(errors, buf);
+ if (!oparam)
+ {
+ _gmx_selparser_error(scanner, "unknown parameter skipped");
+ bOk = FALSE;
+ goto next_param;
+ }
+ if (oparam->flags & SPAR_SET)
+ {
+ _gmx_selparser_error(scanner, "parameter set multiple times, extra values skipped");
+ bOk = FALSE;
+ goto next_param;
+ }
+ oparam->flags |= SPAR_SET;
+ /* Process the values for the parameter */
+ convert_const_values(pparam->value);
+ if (convert_values(pparam->value, oparam->val.type, scanner) != 0)
+ {
+ _gmx_selparser_error(scanner, "invalid value");
+ bOk = FALSE;
+ goto next_param;
+ }
+ if (oparam->val.type == NO_VALUE)
+ {
+ rc = parse_values_bool(pparam->name, pparam->nval, pparam->value, oparam, scanner);
+ }
+ else if (oparam->flags & SPAR_RANGES)
+ {
+ rc = parse_values_range(pparam->nval, pparam->value, oparam, scanner);
+ }
+ else if (oparam->flags & SPAR_VARNUM)
+ {
+ if (pparam->nval == 1 && pparam->value->bExpr)
+ {
+ rc = parse_values_varnum_expr(pparam->nval, pparam->value, oparam, root, scanner);
+ }
+ else
+ {
+ rc = parse_values_varnum(pparam->nval, pparam->value, oparam, root, scanner);
+ }
+ }
+ else if (oparam->flags & SPAR_ENUMVAL)
+ {
+ rc = parse_values_enum(pparam->nval, pparam->value, oparam, scanner);
+ }
+ else
+ {
+ rc = parse_values_std(pparam->nval, pparam->value, oparam, root, scanner);
+ }
+ if (!rc)
+ {
+ bOk = FALSE;
+ }
+ /* Advance to the next parameter */
+next_param:
+ pparam = pparam->next;
+ }
+ /* Check that all required parameters are present */
+ for (i = 0; i < nparam; ++i)
+ {
+ if (!(params[i].flags & SPAR_OPTIONAL) && !(params[i].flags & SPAR_SET))
+ {
+ _gmx_selparser_error(scanner, "required parameter '%s' not specified", params[i].name);
+ bOk = FALSE;
+ }
+ }
+
+ _gmx_selexpr_free_params(pparams);
+ return bOk;
+}
--- /dev/null
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse _gmx_sel_yybparse
+#define yylex _gmx_sel_yyblex
+#define yyerror _gmx_sel_yyberror
+#define yylval _gmx_sel_yyblval
+#define yychar _gmx_sel_yybchar
+#define yydebug _gmx_sel_yybdebug
+#define yynerrs _gmx_sel_yybnerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ INVALID = 258,
+ HELP = 259,
+ HELP_TOPIC = 260,
+ TOK_INT = 261,
+ TOK_REAL = 262,
+ STR = 263,
+ IDENTIFIER = 264,
+ CMD_SEP = 265,
+ GROUP = 266,
+ TO = 267,
+ VARIABLE_NUMERIC = 268,
+ VARIABLE_GROUP = 269,
+ VARIABLE_POS = 270,
+ KEYWORD_NUMERIC = 271,
+ KEYWORD_STR = 272,
+ KEYWORD_POS = 273,
+ KEYWORD_GROUP = 274,
+ METHOD_NUMERIC = 275,
+ METHOD_GROUP = 276,
+ METHOD_POS = 277,
+ MODIFIER = 278,
+ EMPTY_POSMOD = 279,
+ PARAM = 280,
+ END_OF_METHOD = 281,
+ OF = 282,
+ CMP_OP = 283,
+ PARAM_REDUCT = 284,
+ XOR = 285,
+ OR = 286,
+ AND = 287,
+ NOT = 288,
+ UNARY_NEG = 289,
+ NUM_REDUCT = 290
+ };
+#endif
+/* Tokens. */
+#define INVALID 258
+#define HELP 259
+#define HELP_TOPIC 260
+#define TOK_INT 261
+#define TOK_REAL 262
+#define STR 263
+#define IDENTIFIER 264
+#define CMD_SEP 265
+#define GROUP 266
+#define TO 267
+#define VARIABLE_NUMERIC 268
+#define VARIABLE_GROUP 269
+#define VARIABLE_POS 270
+#define KEYWORD_NUMERIC 271
+#define KEYWORD_STR 272
+#define KEYWORD_POS 273
+#define KEYWORD_GROUP 274
+#define METHOD_NUMERIC 275
+#define METHOD_GROUP 276
+#define METHOD_POS 277
+#define MODIFIER 278
+#define EMPTY_POSMOD 279
+#define PARAM 280
+#define END_OF_METHOD 281
+#define OF 282
+#define CMP_OP 283
+#define PARAM_REDUCT 284
+#define XOR 285
+#define OR 286
+#define AND 287
+#define NOT 288
+#define UNARY_NEG 289
+#define NUM_REDUCT 290
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 37 "parser.y"
+
+/*! \internal \file parser.cpp
+ * \brief Generated (from parser.y by Bison) parser for the selection language.
+ *
+ * \ingroup module_selection
+ */
+/*! \internal \file parser.h
+ * \brief Generated (from parser.y by Bison) parser include file.
+ *
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <string2.h>
+
+#include "parsetree.h"
+#include "selelem.h"
+
+#include "scanner.h"
+
+static t_selexpr_value *
+process_value_list(t_selexpr_value *values, int *nr);
+static t_selexpr_param *
+process_param_list(t_selexpr_param *params);
+
+static void
+yyerror(yyscan_t, char const *s);
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 69 "parser.y"
+{
+ int i;
+ real r;
+ char *str;
+ struct gmx_ana_selmethod_t *meth;
+
+ struct t_selelem *sel;
+
+ struct t_selexpr_value *val;
+ struct t_selexpr_param *param;
+}
+/* Line 187 of yacc.c. */
+#line 218 "parser.cpp"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 231 "parser.cpp"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 417
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 49
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 26
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 91
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 150
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 290
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 42, 43, 36, 34, 45, 35, 2, 37, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 41, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 44, 2, 46, 39, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 47, 2, 48, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 38,
+ 40
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 10, 13, 14, 16, 18,
+ 20, 22, 25, 29, 33, 37, 39, 41, 44, 47,
+ 49, 51, 55, 59, 61, 64, 66, 69, 71, 73,
+ 75, 77, 80, 84, 88, 92, 96, 99, 102, 104,
+ 106, 109, 113, 117, 121, 123, 125, 128, 132, 136,
+ 140, 144, 148, 151, 155, 159, 161, 164, 172, 176,
+ 179, 183, 185, 187, 189, 191, 194, 195, 198, 201,
+ 202, 204, 208, 210, 213, 217, 219, 223, 225, 228,
+ 232, 234, 236, 238, 240, 242, 244, 246, 248, 250,
+ 254, 258
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 50, 0, -1, -1, 50, 51, -1, 52, 10, -1,
+ 1, 10, -1, -1, 53, -1, 6, -1, 59, -1,
+ 55, -1, 59, 55, -1, 9, 41, 60, -1, 9,
+ 41, 62, -1, 9, 41, 64, -1, 4, -1, 54,
+ -1, 4, 5, -1, 54, 5, -1, 64, -1, 60,
+ -1, 42, 55, 43, -1, 55, 23, 65, -1, 6,
+ -1, 35, 6, -1, 7, -1, 35, 7, -1, 56,
+ -1, 57, -1, 8, -1, 9, -1, 33, 60, -1,
+ 60, 32, 60, -1, 60, 31, 60, -1, 42, 60,
+ 43, -1, 62, 28, 62, -1, 11, 59, -1, 11,
+ 6, -1, 24, -1, 18, -1, 61, 19, -1, 61,
+ 17, 70, -1, 61, 16, 70, -1, 61, 21, 65,
+ -1, 6, -1, 7, -1, 61, 16, -1, 61, 20,
+ 65, -1, 62, 34, 62, -1, 62, 35, 62, -1,
+ 62, 36, 62, -1, 62, 37, 62, -1, 35, 62,
+ -1, 62, 39, 62, -1, 42, 62, 43, -1, 59,
+ -1, 61, 17, -1, 44, 58, 45, 58, 45, 58,
+ 46, -1, 42, 64, 43, -1, 22, 65, -1, 18,
+ 27, 60, -1, 14, -1, 13, -1, 15, -1, 66,
+ -1, 66, 26, -1, -1, 66, 67, -1, 25, 68,
+ -1, -1, 69, -1, 47, 69, 48, -1, 72, -1,
+ 69, 72, -1, 69, 45, 72, -1, 71, -1, 47,
+ 71, 48, -1, 73, -1, 71, 73, -1, 71, 45,
+ 73, -1, 60, -1, 64, -1, 62, -1, 63, -1,
+ 74, -1, 56, -1, 57, -1, 59, -1, 74, -1,
+ 56, 12, 56, -1, 56, 12, 57, -1, 57, 12,
+ 58, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 189, 189, 190, 199, 200, 220, 224, 225, 234,
+ 244, 246, 248, 250, 252, 258, 259, 262, 263, 267,
+ 268, 273, 274, 286, 287, 291, 292, 295, 296, 299,
+ 300, 308, 314, 320, 332, 336, 344, 350, 358, 359,
+ 363, 368, 373, 381, 393, 400, 410, 415, 423, 425,
+ 427, 429, 431, 433, 435, 442, 449, 461, 466, 470,
+ 478, 489, 493, 497, 506, 508, 513, 514, 519, 526,
+ 527, 528, 532, 533, 535, 540, 541, 545, 546, 548,
+ 552, 554, 556, 558, 560, 564, 569, 574, 579, 583,
+ 588, 593
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "INVALID", "HELP", "HELP_TOPIC",
+ "TOK_INT", "TOK_REAL", "STR", "IDENTIFIER", "CMD_SEP", "GROUP", "TO",
+ "VARIABLE_NUMERIC", "VARIABLE_GROUP", "VARIABLE_POS", "KEYWORD_NUMERIC",
+ "KEYWORD_STR", "KEYWORD_POS", "KEYWORD_GROUP", "METHOD_NUMERIC",
+ "METHOD_GROUP", "METHOD_POS", "MODIFIER", "EMPTY_POSMOD", "PARAM",
+ "END_OF_METHOD", "OF", "CMP_OP", "PARAM_REDUCT", "XOR", "OR", "AND",
+ "NOT", "'+'", "'-'", "'*'", "'/'", "UNARY_NEG", "'^'", "NUM_REDUCT",
+ "'='", "'('", "')'", "'['", "','", "']'", "'{'", "'}'", "$accept",
+ "commands", "command", "cmd_plain", "help_request", "help_topic",
+ "selection", "integer_number", "real_number", "number", "string",
+ "sel_expr", "pos_mod", "num_expr", "str_expr", "pos_expr",
+ "method_params", "method_param_list", "method_param", "value_list",
+ "value_list_contents", "basic_value_list", "basic_value_list_contents",
+ "value_item", "basic_value_item", "value_item_range", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 43, 45, 42, 47, 289, 94,
+ 290, 61, 40, 41, 91, 44, 93, 123, 125
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 49, 50, 50, 51, 51, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 53, 53, 54, 54, 55,
+ 55, 55, 55, 56, 56, 57, 57, 58, 58, 59,
+ 59, 60, 60, 60, 60, 60, 60, 60, 61, 61,
+ 60, 60, 60, 60, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 63, 63, 64, 64, 64,
+ 64, 60, 62, 64, 65, 65, 66, 66, 67, 68,
+ 68, 68, 69, 69, 69, 70, 70, 71, 71, 71,
+ 72, 72, 72, 72, 72, 73, 73, 73, 73, 74,
+ 74, 74
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 2, 0, 1, 1, 1,
+ 1, 2, 3, 3, 3, 1, 1, 2, 2, 1,
+ 1, 3, 3, 1, 2, 1, 2, 1, 1, 1,
+ 1, 2, 3, 3, 3, 3, 2, 2, 1, 1,
+ 2, 3, 3, 3, 1, 1, 2, 3, 3, 3,
+ 3, 3, 2, 3, 3, 1, 2, 7, 3, 2,
+ 3, 1, 1, 1, 1, 2, 0, 2, 2, 0,
+ 1, 3, 1, 2, 3, 1, 3, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
+ 3, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 0, 1, 0, 15, 44, 45, 29, 30, 0,
+ 62, 61, 63, 39, 66, 38, 0, 0, 0, 0,
+ 3, 0, 7, 16, 10, 9, 20, 0, 0, 19,
+ 5, 17, 0, 37, 30, 36, 0, 59, 64, 44,
+ 39, 0, 31, 0, 0, 52, 0, 20, 0, 19,
+ 23, 25, 0, 27, 28, 0, 4, 18, 66, 11,
+ 0, 0, 46, 0, 40, 66, 66, 0, 0, 0,
+ 0, 0, 0, 0, 12, 13, 14, 60, 69, 65,
+ 67, 0, 0, 46, 21, 34, 54, 58, 24, 26,
+ 0, 22, 33, 32, 0, 85, 86, 87, 42, 75,
+ 77, 88, 41, 47, 43, 35, 48, 49, 50, 51,
+ 53, 0, 44, 45, 0, 0, 0, 0, 55, 80,
+ 0, 82, 83, 81, 68, 70, 72, 84, 0, 0,
+ 0, 0, 0, 78, 44, 45, 0, 56, 0, 73,
+ 0, 76, 89, 90, 91, 79, 71, 74, 0, 57
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 20, 21, 22, 23, 24, 95, 96, 55,
+ 97, 119, 27, 28, 122, 123, 37, 38, 80, 124,
+ 125, 102, 99, 126, 100, 101
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -93
+static const yytype_int16 yypact[] =
+{
+ -93, 155, -93, 10, 19, 26, -93, -93, -3, 73,
+ -93, -93, -93, 22, -93, -93, 356, 372, 317, 11,
+ -93, 79, -93, 86, 70, 317, 29, 384, 180, -93,
+ -93, -93, 342, -93, -93, -93, 356, -93, 6, -93,
+ -93, 356, -93, 372, -10, 57, -20, -17, 256, 54,
+ -93, -93, 88, -93, -93, 55, -93, -93, -93, 70,
+ 356, 356, 197, 174, -93, -93, -93, 372, 372, 372,
+ 372, 372, 372, 342, 29, 180, -93, 29, 221, -93,
+ -93, -17, 223, -93, -93, -93, -93, -93, -93, -93,
+ 11, -93, 69, -93, 78, 90, 94, -93, -93, 244,
+ -93, -93, -93, -93, -93, 267, 36, 36, 57, 57,
+ 57, 54, 95, 96, 375, 303, 90, 94, -93, 29,
+ 392, 267, -93, -93, -93, 263, -93, -93, 71, 35,
+ 11, 11, 78, -93, 105, 106, 178, 174, 303, -93,
+ 11, -93, -93, -93, -93, -93, -93, -93, 80, -93
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -93, -93, -93, -93, -93, -93, -13, 0, 14, -81,
+ -1, 87, -4, -16, -93, 3, -36, -93, -93, -93,
+ 12, 81, 39, -91, -92, -67
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -27
+static const yytype_int16 yytable[] =
+{
+ 25, 45, 48, 58, 29, 46, 83, 133, 35, 128,
+ 65, 127, 59, 44, 60, 61, 75, 50, 51, 53,
+ 30, 49, 91, 84, 31, 48, 85, 82, 29, 103,
+ 104, 78, 79, 54, 139, 76, -8, 133, 32, 44,
+ 145, 50, 51, 7, 34, 139, 52, 147, 127, 36,
+ 144, 105, 106, 107, 108, 109, 110, 48, 127, 148,
+ 60, 61, 121, 44, 44, 44, 44, 44, 44, 127,
+ 52, 127, 70, 71, 120, 72, 111, 118, 116, 33,
+ 132, 7, 34, 141, 50, 51, 7, 34, 26, 56,
+ 53, 57, 117, 58, 88, 89, 72, 87, 45, 121,
+ 90, 61, 130, 42, 54, 47, 131, -23, -25, 121,
+ 44, 120, 26, 52, 118, 116, 140, -24, -26, 74,
+ 121, 120, 121, 77, 118, 116, 149, 136, 81, 117,
+ 142, 53, 120, 129, 120, 118, 116, 118, 116, 117,
+ 53, 0, 0, 98, 143, 54, 0, 92, 93, 0,
+ 117, 0, 117, 0, 54, 2, 3, 0, 0, 4,
+ 81, 5, 6, 7, 8, -6, 9, 0, 10, 11,
+ 12, 0, 0, 13, 0, 0, 0, 14, 0, 15,
+ 50, 51, 7, 34, 112, 113, 7, 34, 16, 9,
+ 17, 10, 11, 12, 0, 0, 13, 18, 0, 19,
+ 14, 0, 15, 50, 51, 7, 34, 0, 67, 52,
+ 0, 16, 0, 114, 68, 69, 70, 71, 0, 72,
+ 73, 94, 19, 138, 0, 0, 146, 112, 113, 7,
+ 34, 0, 9, 0, 10, 11, 12, 0, 0, 13,
+ 0, 0, 0, 14, 94, 15, 0, 0, 0, 0,
+ 50, 51, 7, 34, 16, 0, 114, 68, 69, 70,
+ 71, 0, 72, 73, 0, 19, 86, 0, 115, 112,
+ 113, 7, 34, 0, 9, 0, 10, 11, 12, 52,
+ 0, 13, 0, 0, 67, 14, 0, 15, 0, 132,
+ 68, 69, 70, 71, 0, 72, 16, 0, 114, 86,
+ 0, 68, 69, 70, 71, 73, 72, 19, 138, 112,
+ 113, 7, 34, 0, 9, 0, 10, 11, 12, 0,
+ 0, 13, 0, 39, 6, 14, 0, 15, 9, 0,
+ 10, 11, 12, 0, 0, 13, 16, 0, 114, 14,
+ 0, 15, 0, 0, 0, 73, 0, 19, 39, 6,
+ 16, 0, 17, 9, 0, 10, 11, 12, 0, 18,
+ 13, 19, 39, 6, 14, 0, 15, 9, 0, 10,
+ 11, 0, 0, 0, 40, 16, 0, 17, 39, 6,
+ 15, 134, 135, 0, 73, 10, 19, 0, 10, 16,
+ 40, 17, 0, 40, 0, 0, 15, 0, 41, 15,
+ 62, 63, 0, 64, 65, 66, 0, 17, 62, 137,
+ 17, 64, 65, 66, 43, 0, 0, 43
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 1, 17, 18, 23, 1, 18, 16, 99, 9, 90,
+ 20, 78, 25, 17, 31, 32, 32, 6, 7, 19,
+ 10, 18, 58, 43, 5, 41, 43, 43, 25, 65,
+ 66, 25, 26, 19, 125, 32, 10, 129, 41, 43,
+ 132, 6, 7, 8, 9, 136, 35, 138, 115, 27,
+ 131, 67, 68, 69, 70, 71, 72, 73, 125, 140,
+ 31, 32, 78, 67, 68, 69, 70, 71, 72, 136,
+ 35, 138, 36, 37, 78, 39, 73, 78, 78, 6,
+ 45, 8, 9, 48, 6, 7, 8, 9, 1, 10,
+ 90, 5, 78, 23, 6, 7, 39, 43, 114, 115,
+ 45, 32, 12, 16, 90, 18, 12, 12, 12, 125,
+ 114, 115, 25, 35, 115, 115, 45, 12, 12, 32,
+ 136, 125, 138, 36, 125, 125, 46, 115, 41, 115,
+ 130, 131, 136, 94, 138, 136, 136, 138, 138, 125,
+ 140, -1, -1, 62, 130, 131, -1, 60, 61, -1,
+ 136, -1, 138, -1, 140, 0, 1, -1, -1, 4,
+ 73, 6, 7, 8, 9, 10, 11, -1, 13, 14,
+ 15, -1, -1, 18, -1, -1, -1, 22, -1, 24,
+ 6, 7, 8, 9, 6, 7, 8, 9, 33, 11,
+ 35, 13, 14, 15, -1, -1, 18, 42, -1, 44,
+ 22, -1, 24, 6, 7, 8, 9, -1, 28, 35,
+ -1, 33, -1, 35, 34, 35, 36, 37, -1, 39,
+ 42, 47, 44, 45, -1, -1, 48, 6, 7, 8,
+ 9, -1, 11, -1, 13, 14, 15, -1, -1, 18,
+ -1, -1, -1, 22, 47, 24, -1, -1, -1, -1,
+ 6, 7, 8, 9, 33, -1, 35, 34, 35, 36,
+ 37, -1, 39, 42, -1, 44, 43, -1, 47, 6,
+ 7, 8, 9, -1, 11, -1, 13, 14, 15, 35,
+ -1, 18, -1, -1, 28, 22, -1, 24, -1, 45,
+ 34, 35, 36, 37, -1, 39, 33, -1, 35, 43,
+ -1, 34, 35, 36, 37, 42, 39, 44, 45, 6,
+ 7, 8, 9, -1, 11, -1, 13, 14, 15, -1,
+ -1, 18, -1, 6, 7, 22, -1, 24, 11, -1,
+ 13, 14, 15, -1, -1, 18, 33, -1, 35, 22,
+ -1, 24, -1, -1, -1, 42, -1, 44, 6, 7,
+ 33, -1, 35, 11, -1, 13, 14, 15, -1, 42,
+ 18, 44, 6, 7, 22, -1, 24, 11, -1, 13,
+ 14, -1, -1, -1, 18, 33, -1, 35, 6, 7,
+ 24, 6, 7, -1, 42, 13, 44, -1, 13, 33,
+ 18, 35, -1, 18, -1, -1, 24, -1, 42, 24,
+ 16, 17, -1, 19, 20, 21, -1, 35, 16, 17,
+ 35, 19, 20, 21, 42, -1, -1, 42
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 50, 0, 1, 4, 6, 7, 8, 9, 11,
+ 13, 14, 15, 18, 22, 24, 33, 35, 42, 44,
+ 51, 52, 53, 54, 55, 59, 60, 61, 62, 64,
+ 10, 5, 41, 6, 9, 59, 27, 65, 66, 6,
+ 18, 42, 60, 42, 61, 62, 55, 60, 62, 64,
+ 6, 7, 35, 56, 57, 58, 10, 5, 23, 55,
+ 31, 32, 16, 17, 19, 20, 21, 28, 34, 35,
+ 36, 37, 39, 42, 60, 62, 64, 60, 25, 26,
+ 67, 60, 62, 16, 43, 43, 43, 43, 6, 7,
+ 45, 65, 60, 60, 47, 56, 57, 59, 70, 71,
+ 73, 74, 70, 65, 65, 62, 62, 62, 62, 62,
+ 62, 64, 6, 7, 35, 47, 56, 57, 59, 60,
+ 61, 62, 63, 64, 68, 69, 72, 74, 58, 71,
+ 12, 12, 45, 73, 6, 7, 69, 17, 45, 72,
+ 45, 48, 56, 57, 58, 73, 48, 72, 58, 46
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (scanner, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, scanner)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, scanner); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t scanner)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, scanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ yyscan_t scanner;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (scanner);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, yyscan_t scanner)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, scanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ yyscan_t scanner;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, scanner);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule, yyscan_t scanner)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule, scanner)
+ YYSTYPE *yyvsp;
+ int yyrule;
+ yyscan_t scanner;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , scanner);
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule, scanner); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+\f
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+\f
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, yyscan_t scanner)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, scanner)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ yyscan_t scanner;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (scanner);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 5: /* "HELP_TOPIC" */
+#line 168 "parser.y"
+ { free((yyvaluep->str)); };
+#line 1321 "parser.cpp"
+ break;
+ case 8: /* "STR" */
+#line 168 "parser.y"
+ { free((yyvaluep->str)); };
+#line 1326 "parser.cpp"
+ break;
+ case 9: /* "IDENTIFIER" */
+#line 168 "parser.y"
+ { free((yyvaluep->str)); };
+#line 1331 "parser.cpp"
+ break;
+ case 25: /* "PARAM" */
+#line 169 "parser.y"
+ { if((yyvaluep->str)) free((yyvaluep->str)); };
+#line 1336 "parser.cpp"
+ break;
+ case 28: /* "CMP_OP" */
+#line 168 "parser.y"
+ { free((yyvaluep->str)); };
+#line 1341 "parser.cpp"
+ break;
+ case 51: /* "command" */
+#line 170 "parser.y"
+ { if((yyvaluep->sel)) _gmx_selelem_free((yyvaluep->sel)); };
+#line 1346 "parser.cpp"
+ break;
+ case 52: /* "cmd_plain" */
+#line 170 "parser.y"
+ { if((yyvaluep->sel)) _gmx_selelem_free((yyvaluep->sel)); };
+#line 1351 "parser.cpp"
+ break;
+ case 55: /* "selection" */
+#line 171 "parser.y"
+ { _gmx_selelem_free_chain((yyvaluep->sel)); };
+#line 1356 "parser.cpp"
+ break;
+ case 59: /* "string" */
+#line 168 "parser.y"
+ { free((yyvaluep->str)); };
+#line 1361 "parser.cpp"
+ break;
+ case 60: /* "sel_expr" */
+#line 172 "parser.y"
+ { _gmx_selelem_free((yyvaluep->sel)); };
+#line 1366 "parser.cpp"
+ break;
+ case 62: /* "num_expr" */
+#line 172 "parser.y"
+ { _gmx_selelem_free((yyvaluep->sel)); };
+#line 1371 "parser.cpp"
+ break;
+ case 63: /* "str_expr" */
+#line 172 "parser.y"
+ { _gmx_selelem_free((yyvaluep->sel)); };
+#line 1376 "parser.cpp"
+ break;
+ case 64: /* "pos_expr" */
+#line 172 "parser.y"
+ { _gmx_selelem_free((yyvaluep->sel)); };
+#line 1381 "parser.cpp"
+ break;
+ case 65: /* "method_params" */
+#line 173 "parser.y"
+ { _gmx_selexpr_free_params((yyvaluep->param)); };
+#line 1386 "parser.cpp"
+ break;
+ case 66: /* "method_param_list" */
+#line 173 "parser.y"
+ { _gmx_selexpr_free_params((yyvaluep->param)); };
+#line 1391 "parser.cpp"
+ break;
+ case 67: /* "method_param" */
+#line 173 "parser.y"
+ { _gmx_selexpr_free_params((yyvaluep->param)); };
+#line 1396 "parser.cpp"
+ break;
+ case 68: /* "value_list" */
+#line 174 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1401 "parser.cpp"
+ break;
+ case 69: /* "value_list_contents" */
+#line 174 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1406 "parser.cpp"
+ break;
+ case 70: /* "basic_value_list" */
+#line 175 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1411 "parser.cpp"
+ break;
+ case 71: /* "basic_value_list_contents" */
+#line 175 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1416 "parser.cpp"
+ break;
+ case 72: /* "value_item" */
+#line 174 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1421 "parser.cpp"
+ break;
+ case 73: /* "basic_value_item" */
+#line 175 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1426 "parser.cpp"
+ break;
+ case 74: /* "value_item_range" */
+#line 174 "parser.y"
+ { _gmx_selexpr_free_values((yyvaluep->val)); };
+#line 1431 "parser.cpp"
+ break;
+
+ default:
+ break;
+ }
+}
+\f
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (yyscan_t scanner);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (yyscan_t scanner)
+#else
+int
+yyparse (scanner)
+ yyscan_t scanner;
+#endif
+#endif
+{
+ /* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 189 "parser.y"
+ { (yyval.sel) = NULL ;}
+ break;
+
+ case 3:
+#line 191 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_append_selection((yyvsp[(2) - (2)].sel), (yyvsp[(1) - (2)].sel), scanner);
+ if (_gmx_sel_parser_should_finish(scanner))
+ YYACCEPT;
+ ;}
+ break;
+
+ case 4:
+#line 199 "parser.y"
+ { (yyval.sel) = (yyvsp[(1) - (2)].sel); ;}
+ break;
+
+ case 5:
+#line 201 "parser.y"
+ {
+ (yyval.sel) = NULL;
+ _gmx_selparser_error(scanner, "invalid selection '%s'",
+ _gmx_sel_lexer_pselstr(scanner));
+ _gmx_sel_lexer_clear_method_stack(scanner);
+ if (_gmx_sel_is_lexer_interactive(scanner))
+ {
+ _gmx_sel_lexer_clear_pselstr(scanner);
+ yyerrok;
+ }
+ else
+ {
+ YYABORT;
+ }
+ ;}
+ break;
+
+ case 6:
+#line 220 "parser.y"
+ {
+ (yyval.sel) = NULL;
+ _gmx_sel_handle_empty_cmd(scanner);
+ ;}
+ break;
+
+ case 7:
+#line 224 "parser.y"
+ { (yyval.sel) = NULL; ;}
+ break;
+
+ case 8:
+#line 226 "parser.y"
+ {
+ t_selelem *s, *p;
+ s = _gmx_sel_init_group_by_id((yyvsp[(1) - (1)].i), scanner);
+ if (s == NULL) YYERROR;
+ p = _gmx_sel_init_position(s, NULL, scanner);
+ if (p == NULL) YYERROR;
+ (yyval.sel) = _gmx_sel_init_selection(strdup(s->name), p, scanner);
+ ;}
+ break;
+
+ case 9:
+#line 235 "parser.y"
+ {
+ t_selelem *s, *p;
+ s = _gmx_sel_init_group_by_name((yyvsp[(1) - (1)].str), scanner);
+ free((yyvsp[(1) - (1)].str));
+ if (s == NULL) YYERROR;
+ p = _gmx_sel_init_position(s, NULL, scanner);
+ if (p == NULL) YYERROR;
+ (yyval.sel) = _gmx_sel_init_selection(strdup(s->name), p, scanner);
+ ;}
+ break;
+
+ case 10:
+#line 245 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_selection(NULL, (yyvsp[(1) - (1)].sel), scanner); ;}
+ break;
+
+ case 11:
+#line 247 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_selection((yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].sel), scanner); ;}
+ break;
+
+ case 12:
+#line 249 "parser.y"
+ { (yyval.sel) = _gmx_sel_assign_variable((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].sel), scanner); ;}
+ break;
+
+ case 13:
+#line 251 "parser.y"
+ { (yyval.sel) = _gmx_sel_assign_variable((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].sel), scanner); ;}
+ break;
+
+ case 14:
+#line 253 "parser.y"
+ { (yyval.sel) = _gmx_sel_assign_variable((yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].sel), scanner); ;}
+ break;
+
+ case 15:
+#line 258 "parser.y"
+ { _gmx_sel_handle_help_cmd(NULL, scanner); ;}
+ break;
+
+ case 17:
+#line 262 "parser.y"
+ { _gmx_sel_handle_help_cmd((yyvsp[(2) - (2)].str), scanner); ;}
+ break;
+
+ case 18:
+#line 263 "parser.y"
+ { _gmx_sel_handle_help_cmd((yyvsp[(2) - (2)].str), scanner); ;}
+ break;
+
+ case 19:
+#line 267 "parser.y"
+ { (yyval.sel) = (yyvsp[(1) - (1)].sel); ;}
+ break;
+
+ case 20:
+#line 269 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_position((yyvsp[(1) - (1)].sel), NULL, scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 21:
+#line 273 "parser.y"
+ { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
+ break;
+
+ case 22:
+#line 275 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_modifier((yyvsp[(2) - (3)].meth), (yyvsp[(3) - (3)].param), (yyvsp[(1) - (3)].sel), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 23:
+#line 286 "parser.y"
+ { (yyval.r) = (yyvsp[(1) - (1)].i); ;}
+ break;
+
+ case 24:
+#line 287 "parser.y"
+ { (yyval.r) = -(yyvsp[(2) - (2)].i); ;}
+ break;
+
+ case 25:
+#line 291 "parser.y"
+ { (yyval.r) = (yyvsp[(1) - (1)].r); ;}
+ break;
+
+ case 26:
+#line 292 "parser.y"
+ { (yyval.r) = -(yyvsp[(2) - (2)].r); ;}
+ break;
+
+ case 27:
+#line 295 "parser.y"
+ { (yyval.r) = (yyvsp[(1) - (1)].r); ;}
+ break;
+
+ case 28:
+#line 296 "parser.y"
+ { (yyval.r) = (yyvsp[(1) - (1)].r); ;}
+ break;
+
+ case 29:
+#line 299 "parser.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
+ break;
+
+ case 30:
+#line 300 "parser.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
+ break;
+
+ case 31:
+#line 309 "parser.y"
+ {
+ (yyval.sel) = _gmx_selelem_create(SEL_BOOLEAN);
+ (yyval.sel)->u.boolt = BOOL_NOT;
+ (yyval.sel)->child = (yyvsp[(2) - (2)].sel);
+ ;}
+ break;
+
+ case 32:
+#line 315 "parser.y"
+ {
+ (yyval.sel) = _gmx_selelem_create(SEL_BOOLEAN);
+ (yyval.sel)->u.boolt = BOOL_AND;
+ (yyval.sel)->child = (yyvsp[(1) - (3)].sel); (yyval.sel)->child->next = (yyvsp[(3) - (3)].sel);
+ ;}
+ break;
+
+ case 33:
+#line 321 "parser.y"
+ {
+ (yyval.sel) = _gmx_selelem_create(SEL_BOOLEAN);
+ (yyval.sel)->u.boolt = BOOL_OR;
+ (yyval.sel)->child = (yyvsp[(1) - (3)].sel); (yyval.sel)->child->next = (yyvsp[(3) - (3)].sel);
+ ;}
+ break;
+
+ case 34:
+#line 332 "parser.y"
+ { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
+ break;
+
+ case 35:
+#line 337 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_comparison((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), (yyvsp[(2) - (3)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 36:
+#line 345 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_group_by_name((yyvsp[(2) - (2)].str), scanner);
+ free((yyvsp[(2) - (2)].str));
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 37:
+#line 351 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_group_by_id((yyvsp[(2) - (2)].i), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 38:
+#line 358 "parser.y"
+ { (yyval.str) = NULL; ;}
+ break;
+
+ case 39:
+#line 359 "parser.y"
+ { (yyval.str) = (yyvsp[(1) - (1)].str); ;}
+ break;
+
+ case 40:
+#line 364 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (2)].meth), NULL, (yyvsp[(1) - (2)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 41:
+#line 369 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (3)].meth), process_value_list((yyvsp[(3) - (3)].val), NULL), (yyvsp[(1) - (3)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 42:
+#line 374 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (3)].meth), process_value_list((yyvsp[(3) - (3)].val), NULL), (yyvsp[(1) - (3)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 43:
+#line 382 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_method((yyvsp[(2) - (3)].meth), (yyvsp[(3) - (3)].param), (yyvsp[(1) - (3)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 44:
+#line 394 "parser.y"
+ {
+ (yyval.sel) = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype((yyval.sel), INT_VALUE);
+ _gmx_selvalue_reserve(&(yyval.sel)->v, 1);
+ (yyval.sel)->v.u.i[0] = (yyvsp[(1) - (1)].i);
+ ;}
+ break;
+
+ case 45:
+#line 401 "parser.y"
+ {
+ (yyval.sel) = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype((yyval.sel), REAL_VALUE);
+ _gmx_selvalue_reserve(&(yyval.sel)->v, 1);
+ (yyval.sel)->v.u.r[0] = (yyvsp[(1) - (1)].r);
+ ;}
+ break;
+
+ case 46:
+#line 411 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (2)].meth), NULL, (yyvsp[(1) - (2)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 47:
+#line 416 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_method((yyvsp[(2) - (3)].meth), (yyvsp[(3) - (3)].param), (yyvsp[(1) - (3)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 48:
+#line 424 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '+', scanner); ;}
+ break;
+
+ case 49:
+#line 426 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '-', scanner); ;}
+ break;
+
+ case 50:
+#line 428 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '*', scanner); ;}
+ break;
+
+ case 51:
+#line 430 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '/', scanner); ;}
+ break;
+
+ case 52:
+#line 432 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(2) - (2)].sel), NULL, '-', scanner); ;}
+ break;
+
+ case 53:
+#line 434 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_arithmetic((yyvsp[(1) - (3)].sel), (yyvsp[(3) - (3)].sel), '^', scanner); ;}
+ break;
+
+ case 54:
+#line 435 "parser.y"
+ { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
+ break;
+
+ case 55:
+#line 443 "parser.y"
+ {
+ (yyval.sel) = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype((yyval.sel), STR_VALUE);
+ _gmx_selvalue_reserve(&(yyval.sel)->v, 1);
+ (yyval.sel)->v.u.s[0] = (yyvsp[(1) - (1)].str);
+ ;}
+ break;
+
+ case 56:
+#line 450 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_keyword((yyvsp[(2) - (2)].meth), NULL, (yyvsp[(1) - (2)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 57:
+#line 462 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_const_position((yyvsp[(2) - (7)].r), (yyvsp[(4) - (7)].r), (yyvsp[(6) - (7)].r)); ;}
+ break;
+
+ case 58:
+#line 466 "parser.y"
+ { (yyval.sel) = (yyvsp[(2) - (3)].sel); ;}
+ break;
+
+ case 59:
+#line 471 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_method((yyvsp[(1) - (2)].meth), (yyvsp[(2) - (2)].param), NULL, scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 60:
+#line 479 "parser.y"
+ {
+ (yyval.sel) = _gmx_sel_init_position((yyvsp[(3) - (3)].sel), (yyvsp[(1) - (3)].str), scanner);
+ if ((yyval.sel) == NULL) YYERROR;
+ ;}
+ break;
+
+ case 61:
+#line 490 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_variable_ref((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 62:
+#line 494 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_variable_ref((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 63:
+#line 498 "parser.y"
+ { (yyval.sel) = _gmx_sel_init_variable_ref((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 64:
+#line 507 "parser.y"
+ { (yyval.param) = process_param_list((yyvsp[(1) - (1)].param)); ;}
+ break;
+
+ case 65:
+#line 509 "parser.y"
+ { (yyval.param) = process_param_list((yyvsp[(1) - (2)].param)); ;}
+ break;
+
+ case 66:
+#line 513 "parser.y"
+ { (yyval.param) = NULL; ;}
+ break;
+
+ case 67:
+#line 515 "parser.y"
+ { (yyvsp[(2) - (2)].param)->next = (yyvsp[(1) - (2)].param); (yyval.param) = (yyvsp[(2) - (2)].param); ;}
+ break;
+
+ case 68:
+#line 520 "parser.y"
+ {
+ (yyval.param) = _gmx_selexpr_create_param((yyvsp[(1) - (2)].str));
+ (yyval.param)->value = process_value_list((yyvsp[(2) - (2)].val), &(yyval.param)->nval);
+ ;}
+ break;
+
+ case 69:
+#line 526 "parser.y"
+ { (yyval.val) = NULL; ;}
+ break;
+
+ case 70:
+#line 527 "parser.y"
+ { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
+ break;
+
+ case 71:
+#line 528 "parser.y"
+ { (yyval.val) = (yyvsp[(2) - (3)].val); ;}
+ break;
+
+ case 72:
+#line 532 "parser.y"
+ { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
+ break;
+
+ case 73:
+#line 534 "parser.y"
+ { (yyvsp[(2) - (2)].val)->next = (yyvsp[(1) - (2)].val); (yyval.val) = (yyvsp[(2) - (2)].val); ;}
+ break;
+
+ case 74:
+#line 536 "parser.y"
+ { (yyvsp[(3) - (3)].val)->next = (yyvsp[(1) - (3)].val); (yyval.val) = (yyvsp[(3) - (3)].val); ;}
+ break;
+
+ case 75:
+#line 540 "parser.y"
+ { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
+ break;
+
+ case 76:
+#line 541 "parser.y"
+ { (yyval.val) = (yyvsp[(2) - (3)].val); ;}
+ break;
+
+ case 77:
+#line 545 "parser.y"
+ { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
+ break;
+
+ case 78:
+#line 547 "parser.y"
+ { (yyvsp[(2) - (2)].val)->next = (yyvsp[(1) - (2)].val); (yyval.val) = (yyvsp[(2) - (2)].val); ;}
+ break;
+
+ case 79:
+#line 549 "parser.y"
+ { (yyvsp[(3) - (3)].val)->next = (yyvsp[(1) - (3)].val); (yyval.val) = (yyvsp[(3) - (3)].val); ;}
+ break;
+
+ case 80:
+#line 553 "parser.y"
+ { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 81:
+#line 555 "parser.y"
+ { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 82:
+#line 557 "parser.y"
+ { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 83:
+#line 559 "parser.y"
+ { (yyval.val) = _gmx_selexpr_create_value_expr((yyvsp[(1) - (1)].sel)); ;}
+ break;
+
+ case 84:
+#line 560 "parser.y"
+ { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
+ break;
+
+ case 85:
+#line 565 "parser.y"
+ {
+ (yyval.val) = _gmx_selexpr_create_value(INT_VALUE);
+ (yyval.val)->u.i.i1 = (yyval.val)->u.i.i2 = (yyvsp[(1) - (1)].r);
+ ;}
+ break;
+
+ case 86:
+#line 570 "parser.y"
+ {
+ (yyval.val) = _gmx_selexpr_create_value(REAL_VALUE);
+ (yyval.val)->u.r.r1 = (yyval.val)->u.r.r2 = (yyvsp[(1) - (1)].r);
+ ;}
+ break;
+
+ case 87:
+#line 575 "parser.y"
+ {
+ (yyval.val) = _gmx_selexpr_create_value(STR_VALUE);
+ (yyval.val)->u.s = (yyvsp[(1) - (1)].str);
+ ;}
+ break;
+
+ case 88:
+#line 579 "parser.y"
+ { (yyval.val) = (yyvsp[(1) - (1)].val); ;}
+ break;
+
+ case 89:
+#line 584 "parser.y"
+ {
+ (yyval.val) = _gmx_selexpr_create_value(INT_VALUE);
+ (yyval.val)->u.i.i1 = (yyvsp[(1) - (3)].r); (yyval.val)->u.i.i2 = (yyvsp[(3) - (3)].r);
+ ;}
+ break;
+
+ case 90:
+#line 589 "parser.y"
+ {
+ (yyval.val) = _gmx_selexpr_create_value(REAL_VALUE);
+ (yyval.val)->u.r.r1 = (yyvsp[(1) - (3)].r); (yyval.val)->u.r.r2 = (yyvsp[(3) - (3)].r);
+ ;}
+ break;
+
+ case 91:
+#line 594 "parser.y"
+ {
+ (yyval.val) = _gmx_selexpr_create_value(REAL_VALUE);
+ (yyval.val)->u.r.r1 = (yyvsp[(1) - (3)].r); (yyval.val)->u.r.r2 = (yyvsp[(3) - (3)].r);
+ ;}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 2313 "parser.cpp"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (scanner, YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (scanner, yymsg);
+ }
+ else
+ {
+ yyerror (scanner, YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, scanner);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, scanner);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (scanner, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, scanner);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, scanner);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 600 "parser.y"
+
+
+static t_selexpr_value *
+process_value_list(t_selexpr_value *values, int *nr)
+{
+ t_selexpr_value *val, *pval, *nval;
+
+ /* Count values (if needed) and reverse list */
+ if (nr)
+ {
+ *nr = 0;
+ }
+ pval = NULL;
+ val = values;
+ while (val)
+ {
+ if (nr)
+ {
+ ++*nr;
+ }
+ nval = val->next;
+ val->next = pval;
+ pval = val;
+ val = nval;
+ }
+ values = pval;
+
+ return values;
+}
+
+static t_selexpr_param *
+process_param_list(t_selexpr_param *params)
+{
+ t_selexpr_param *par, *ppar, *npar;
+
+ /* Reverse list */
+ ppar = NULL;
+ par = params;
+ while (par)
+ {
+ npar = par->next;
+ par->next = ppar;
+ ppar = par;
+ par = npar;
+ }
+ params = ppar;
+
+ return params;
+}
+
+static void
+yyerror(yyscan_t scanner, char const *s)
+{
+ _gmx_selparser_error(scanner, "%s", s);
+}
+
+
+
--- /dev/null
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ INVALID = 258,
+ HELP = 259,
+ HELP_TOPIC = 260,
+ TOK_INT = 261,
+ TOK_REAL = 262,
+ STR = 263,
+ IDENTIFIER = 264,
+ CMD_SEP = 265,
+ GROUP = 266,
+ TO = 267,
+ VARIABLE_NUMERIC = 268,
+ VARIABLE_GROUP = 269,
+ VARIABLE_POS = 270,
+ KEYWORD_NUMERIC = 271,
+ KEYWORD_STR = 272,
+ KEYWORD_POS = 273,
+ KEYWORD_GROUP = 274,
+ METHOD_NUMERIC = 275,
+ METHOD_GROUP = 276,
+ METHOD_POS = 277,
+ MODIFIER = 278,
+ EMPTY_POSMOD = 279,
+ PARAM = 280,
+ END_OF_METHOD = 281,
+ OF = 282,
+ CMP_OP = 283,
+ PARAM_REDUCT = 284,
+ XOR = 285,
+ OR = 286,
+ AND = 287,
+ NOT = 288,
+ UNARY_NEG = 289,
+ NUM_REDUCT = 290
+ };
+#endif
+/* Tokens. */
+#define INVALID 258
+#define HELP 259
+#define HELP_TOPIC 260
+#define TOK_INT 261
+#define TOK_REAL 262
+#define STR 263
+#define IDENTIFIER 264
+#define CMD_SEP 265
+#define GROUP 266
+#define TO 267
+#define VARIABLE_NUMERIC 268
+#define VARIABLE_GROUP 269
+#define VARIABLE_POS 270
+#define KEYWORD_NUMERIC 271
+#define KEYWORD_STR 272
+#define KEYWORD_POS 273
+#define KEYWORD_GROUP 274
+#define METHOD_NUMERIC 275
+#define METHOD_GROUP 276
+#define METHOD_POS 277
+#define MODIFIER 278
+#define EMPTY_POSMOD 279
+#define PARAM 280
+#define END_OF_METHOD 281
+#define OF 282
+#define CMP_OP 283
+#define PARAM_REDUCT 284
+#define XOR 285
+#define OR 286
+#define AND 287
+#define NOT 288
+#define UNARY_NEG 289
+#define NUM_REDUCT 290
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 69 "parser.y"
+{
+ int i;
+ real r;
+ char *str;
+ struct gmx_ana_selmethod_t *meth;
+
+ struct t_selelem *sel;
+
+ struct t_selexpr_value *val;
+ struct t_selexpr_param *param;
+}
+/* Line 1489 of yacc.c. */
+#line 131 "parser.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Grammar description and parser for the selection language.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+%{
+/*! \internal \file parser.cpp
+ * \brief Generated (from parser.y by Bison) parser for the selection language.
+ *
+ * \ingroup module_selection
+ */
+/*! \internal \file parser.h
+ * \brief Generated (from parser.y by Bison) parser include file.
+ *
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <string2.h>
+
+#include "parsetree.h"
+#include "selelem.h"
+
+#include "scanner.h"
+
+static t_selexpr_value *
+process_value_list(t_selexpr_value *values, int *nr);
+static t_selexpr_param *
+process_param_list(t_selexpr_param *params);
+
+static void
+yyerror(yyscan_t, char const *s);
+%}
+
+%union{
+ int i;
+ real r;
+ char *str;
+ struct gmx_ana_selmethod_t *meth;
+
+ struct t_selelem *sel;
+
+ struct t_selexpr_value *val;
+ struct t_selexpr_param *param;
+};
+/* NOTE: The Intel compiler seems to report warnings for the above line about
+ * "definition at end of file not followed by a semicolon or a declarator".
+ * This is due to the compiler misinterpreting #line directives in the
+ * generated files parser.c/.h, and changing them would be more trouble than
+ * it's worth. */
+
+/* Invalid token to report lexer errors */
+%token INVALID
+
+/* Tokens for help requests */
+%token HELP
+%token <str> HELP_TOPIC
+
+/* Simple input tokens */
+%token <i> TOK_INT
+%token <r> TOK_REAL
+%token <str> STR
+%token <str> IDENTIFIER
+%token CMD_SEP
+
+/* Simple keyword tokens */
+%token GROUP
+%token TO
+
+/* Variable tokens */
+%token <sel> VARIABLE_NUMERIC
+%token <sel> VARIABLE_GROUP
+%token <sel> VARIABLE_POS
+
+/* Selection method tokens */
+%token <meth> KEYWORD_NUMERIC
+%token <meth> KEYWORD_STR
+%token <str> KEYWORD_POS
+%token <meth> KEYWORD_GROUP
+%token <meth> METHOD_NUMERIC
+%token <meth> METHOD_GROUP
+%token <meth> METHOD_POS
+%token <meth> MODIFIER
+/* Empty token that should precede any non-position KEYWORD/METHOD token that
+ * is not preceded by KEYWORD_POS. This is used to work around reduce/reduce
+ * conflicts that appear when a lookahead token would require a reduction of
+ * a rule with empty RHS before shifting, and there is an alternative reduction
+ * available. Replacing the empty RHS with a dummy token makes these conflicts
+ * only shift/reduce conflicts. Another alternative would be to remove the
+ * pos_mod non-terminal completely and split each rule that uses it into two,
+ * but this would require duplicating six rules in the grammar. */
+%token EMPTY_POSMOD
+
+%token <str> PARAM
+%token END_OF_METHOD
+
+%token OF
+/* Comparison operators have lower precedence than parameter reduction
+ * to make it possible to parse, e.g., "mindist from resnr 1 < 2" without
+ * parenthesis. */
+%nonassoc <str> CMP_OP
+/* A dummy token that determines the precedence of parameter reduction */
+%nonassoc PARAM_REDUCT
+/* Boolean operator tokens */
+%left OR XOR
+%left AND
+%left NOT
+/* Arithmetic operator tokens */
+%left '+' '-'
+%left '*' '/'
+%right UNARY_NEG /* Dummy token for unary negation precedence */
+%right '^'
+%nonassoc NUM_REDUCT /* Dummy token for numerical keyword reduction precedence */
+
+/* Simple non-terminals */
+%type <r> integer_number
+%type <r> real_number number
+%type <str> string
+%type <str> pos_mod
+
+/* Expression non-terminals */
+%type <sel> commands command cmd_plain
+%type <sel> selection
+%type <sel> sel_expr
+%type <sel> num_expr
+%type <sel> str_expr
+%type <sel> pos_expr
+
+/* Parameter/value non-terminals */
+%type <param> method_params method_param_list method_param
+%type <val> value_list value_list_contents value_item value_item_range
+%type <val> basic_value_list basic_value_list_contents basic_value_item
+
+%destructor { free($$); } HELP_TOPIC STR IDENTIFIER CMP_OP string
+%destructor { if($$) free($$); } PARAM
+%destructor { if($$) _gmx_selelem_free($$); } command cmd_plain
+%destructor { _gmx_selelem_free_chain($$); } selection
+%destructor { _gmx_selelem_free($$); } sel_expr num_expr str_expr pos_expr
+%destructor { _gmx_selexpr_free_params($$); } method_params method_param_list method_param
+%destructor { _gmx_selexpr_free_values($$); } value_list value_list_contents value_item value_item_range
+%destructor { _gmx_selexpr_free_values($$); } basic_value_list basic_value_list_contents basic_value_item
+
+%expect 50
+%debug
+%pure-parser
+
+/* If you change these, you also need to update the prototype in parsetree.c. */
+%name-prefix="_gmx_sel_yyb"
+%parse-param { yyscan_t scanner }
+%lex-param { yyscan_t scanner }
+
+%%
+
+/* The start rule: allow one or more commands */
+commands: /* empty */ { $$ = NULL }
+ | commands command
+ {
+ $$ = _gmx_sel_append_selection($2, $1, scanner);
+ if (_gmx_sel_parser_should_finish(scanner))
+ YYACCEPT;
+ }
+;
+
+/* A command is formed from an actual command and a separator */
+command: cmd_plain CMD_SEP { $$ = $1; }
+ | error CMD_SEP
+ {
+ $$ = NULL;
+ _gmx_selparser_error(scanner, "invalid selection '%s'",
+ _gmx_sel_lexer_pselstr(scanner));
+ _gmx_sel_lexer_clear_method_stack(scanner);
+ if (_gmx_sel_is_lexer_interactive(scanner))
+ {
+ _gmx_sel_lexer_clear_pselstr(scanner);
+ yyerrok;
+ }
+ else
+ {
+ YYABORT;
+ }
+ }
+;
+
+/* Commands can be selections or variable assignments */
+cmd_plain: /* empty */
+ {
+ $$ = NULL;
+ _gmx_sel_handle_empty_cmd(scanner);
+ }
+ | help_request { $$ = NULL; }
+ | TOK_INT
+ {
+ t_selelem *s, *p;
+ s = _gmx_sel_init_group_by_id($1, scanner);
+ if (s == NULL) YYERROR;
+ p = _gmx_sel_init_position(s, NULL, scanner);
+ if (p == NULL) YYERROR;
+ $$ = _gmx_sel_init_selection(strdup(s->name), p, scanner);
+ }
+ | string
+ {
+ t_selelem *s, *p;
+ s = _gmx_sel_init_group_by_name($1, scanner);
+ free($1);
+ if (s == NULL) YYERROR;
+ p = _gmx_sel_init_position(s, NULL, scanner);
+ if (p == NULL) YYERROR;
+ $$ = _gmx_sel_init_selection(strdup(s->name), p, scanner);
+ }
+ | selection
+ { $$ = _gmx_sel_init_selection(NULL, $1, scanner); }
+ | string selection
+ { $$ = _gmx_sel_init_selection($1, $2, scanner); }
+ | IDENTIFIER '=' sel_expr
+ { $$ = _gmx_sel_assign_variable($1, $3, scanner); }
+ | IDENTIFIER '=' num_expr
+ { $$ = _gmx_sel_assign_variable($1, $3, scanner); }
+ | IDENTIFIER '=' pos_expr
+ { $$ = _gmx_sel_assign_variable($1, $3, scanner); }
+;
+
+/* Help requests */
+help_request:
+ HELP { _gmx_sel_handle_help_cmd(NULL, scanner); }
+ | help_topic
+;
+
+help_topic: HELP HELP_TOPIC { _gmx_sel_handle_help_cmd($2, scanner); }
+ | help_topic HELP_TOPIC { _gmx_sel_handle_help_cmd($2, scanner); }
+;
+
+/* Selection is made of an expression and zero or more modifiers */
+selection: pos_expr { $$ = $1; }
+ | sel_expr
+ {
+ $$ = _gmx_sel_init_position($1, NULL, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+ | '(' selection ')' { $$ = $2; }
+ | selection MODIFIER method_params
+ {
+ $$ = _gmx_sel_init_modifier($2, $3, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/********************************************************************
+ * BASIC NON-TERMINAL SYMBOLS
+ ********************************************************************/
+
+integer_number:
+ TOK_INT { $$ = $1; }
+ | '-' TOK_INT { $$ = -$2; }
+;
+
+real_number:
+ TOK_REAL { $$ = $1; }
+ | '-' TOK_REAL { $$ = -$2; }
+;
+
+number: integer_number { $$ = $1; }
+ | real_number { $$ = $1; }
+;
+
+string: STR { $$ = $1; }
+ | IDENTIFIER { $$ = $1; }
+;
+
+/********************************************************************
+ * ATOM SELECTION EXPRESSIONS
+ ********************************************************************/
+
+/* Boolean expressions and grouping */
+sel_expr: NOT sel_expr
+ {
+ $$ = _gmx_selelem_create(SEL_BOOLEAN);
+ $$->u.boolt = BOOL_NOT;
+ $$->child = $2;
+ }
+ | sel_expr AND sel_expr
+ {
+ $$ = _gmx_selelem_create(SEL_BOOLEAN);
+ $$->u.boolt = BOOL_AND;
+ $$->child = $1; $$->child->next = $3;
+ }
+ | sel_expr OR sel_expr
+ {
+ $$ = _gmx_selelem_create(SEL_BOOLEAN);
+ $$->u.boolt = BOOL_OR;
+ $$->child = $1; $$->child->next = $3;
+ }
+/* | sel_expr XOR sel_expr
+ {
+ $$ = _gmx_selelem_create(SEL_BOOLEAN);
+ $$->u.boolt = BOOL_XOR;
+ $$->child = $1; $$->child->next = $3;
+ }*/
+ | '(' sel_expr ')' { $$ = $2; }
+;
+
+/* Numeric comparisons */
+sel_expr: num_expr CMP_OP num_expr
+ {
+ $$ = _gmx_sel_init_comparison($1, $3, $2, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/* External groups */
+sel_expr: GROUP string
+ {
+ $$ = _gmx_sel_init_group_by_name($2, scanner);
+ free($2);
+ if ($$ == NULL) YYERROR;
+ }
+ | GROUP TOK_INT
+ {
+ $$ = _gmx_sel_init_group_by_id($2, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/* Position modifiers for selection methods */
+pos_mod: EMPTY_POSMOD { $$ = NULL; }
+ | KEYWORD_POS { $$ = $1; }
+;
+
+/* Keyword selections */
+sel_expr: pos_mod KEYWORD_GROUP
+ {
+ $$ = _gmx_sel_init_keyword($2, NULL, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+ | pos_mod KEYWORD_STR basic_value_list
+ {
+ $$ = _gmx_sel_init_keyword($2, process_value_list($3, NULL), $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+ | pos_mod KEYWORD_NUMERIC basic_value_list
+ {
+ $$ = _gmx_sel_init_keyword($2, process_value_list($3, NULL), $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/* Custom selection methods */
+sel_expr: pos_mod METHOD_GROUP method_params
+ {
+ $$ = _gmx_sel_init_method($2, $3, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/********************************************************************
+ * NUMERICAL EXPRESSIONS
+ ********************************************************************/
+
+/* Basic numerical values */
+num_expr: TOK_INT
+ {
+ $$ = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype($$, INT_VALUE);
+ _gmx_selvalue_reserve(&$$->v, 1);
+ $$->v.u.i[0] = $1;
+ }
+ | TOK_REAL
+ {
+ $$ = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype($$, REAL_VALUE);
+ _gmx_selvalue_reserve(&$$->v, 1);
+ $$->v.u.r[0] = $1;
+ }
+;
+
+/* Numeric selection methods */
+num_expr: pos_mod KEYWORD_NUMERIC %prec NUM_REDUCT
+ {
+ $$ = _gmx_sel_init_keyword($2, NULL, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+ | pos_mod METHOD_NUMERIC method_params
+ {
+ $$ = _gmx_sel_init_method($2, $3, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/* Arithmetic evaluation and grouping */
+num_expr: num_expr '+' num_expr
+ { $$ = _gmx_sel_init_arithmetic($1, $3, '+', scanner); }
+ | num_expr '-' num_expr
+ { $$ = _gmx_sel_init_arithmetic($1, $3, '-', scanner); }
+ | num_expr '*' num_expr
+ { $$ = _gmx_sel_init_arithmetic($1, $3, '*', scanner); }
+ | num_expr '/' num_expr
+ { $$ = _gmx_sel_init_arithmetic($1, $3, '/', scanner); }
+ | '-' num_expr %prec UNARY_NEG
+ { $$ = _gmx_sel_init_arithmetic($2, NULL, '-', scanner); }
+ | num_expr '^' num_expr
+ { $$ = _gmx_sel_init_arithmetic($1, $3, '^', scanner); }
+ | '(' num_expr ')' { $$ = $2; }
+;
+
+/********************************************************************
+ * STRING EXPRESSIONS
+ ********************************************************************/
+
+str_expr: string
+ {
+ $$ = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype($$, STR_VALUE);
+ _gmx_selvalue_reserve(&$$->v, 1);
+ $$->v.u.s[0] = $1;
+ }
+ | pos_mod KEYWORD_STR
+ {
+ $$ = _gmx_sel_init_keyword($2, NULL, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/********************************************************************
+ * POSITION EXPRESSIONS
+ ********************************************************************/
+
+/* Constant position expressions */
+pos_expr: '[' number ',' number ',' number ']'
+ { $$ = _gmx_sel_init_const_position($2, $4, $6); }
+;
+
+/* Grouping of position expressions */
+pos_expr: '(' pos_expr ')' { $$ = $2; }
+;
+
+/* Expressions with a position value */
+pos_expr: METHOD_POS method_params
+ {
+ $$ = _gmx_sel_init_method($1, $2, NULL, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/* Evaluation of positions using a keyword */
+pos_expr: KEYWORD_POS OF sel_expr %prec PARAM_REDUCT
+ {
+ $$ = _gmx_sel_init_position($3, $1, scanner);
+ if ($$ == NULL) YYERROR;
+ }
+;
+
+/********************************************************************
+ * VARIABLES
+ ********************************************************************/
+
+sel_expr: VARIABLE_GROUP
+ { $$ = _gmx_sel_init_variable_ref($1); }
+;
+
+num_expr: VARIABLE_NUMERIC
+ { $$ = _gmx_sel_init_variable_ref($1); }
+;
+
+pos_expr: VARIABLE_POS
+ { $$ = _gmx_sel_init_variable_ref($1); }
+;
+
+/********************************************************************
+ * METHOD PARAMETERS
+ ********************************************************************/
+
+method_params:
+ method_param_list
+ { $$ = process_param_list($1); }
+ | method_param_list END_OF_METHOD
+ { $$ = process_param_list($1); }
+;
+
+method_param_list:
+ /* empty */ { $$ = NULL; }
+ | method_param_list method_param
+ { $2->next = $1; $$ = $2; }
+;
+
+method_param:
+ PARAM value_list
+ {
+ $$ = _gmx_selexpr_create_param($1);
+ $$->value = process_value_list($2, &$$->nval);
+ }
+;
+
+value_list: /* empty */ { $$ = NULL; }
+ | value_list_contents { $$ = $1; }
+ | '{' value_list_contents '}' { $$ = $2; }
+;
+
+value_list_contents:
+ value_item { $$ = $1; }
+ | value_list_contents value_item
+ { $2->next = $1; $$ = $2; }
+ | value_list_contents ',' value_item
+ { $3->next = $1; $$ = $3; }
+;
+
+basic_value_list:
+ basic_value_list_contents { $$ = $1; }
+ | '{' basic_value_list_contents '}' { $$ = $2; }
+;
+
+basic_value_list_contents:
+ basic_value_item { $$ = $1; }
+ | basic_value_list_contents basic_value_item
+ { $2->next = $1; $$ = $2; }
+ | basic_value_list_contents ',' basic_value_item
+ { $3->next = $1; $$ = $3; }
+;
+
+value_item: sel_expr %prec PARAM_REDUCT
+ { $$ = _gmx_selexpr_create_value_expr($1); }
+ | pos_expr %prec PARAM_REDUCT
+ { $$ = _gmx_selexpr_create_value_expr($1); }
+ | num_expr %prec PARAM_REDUCT
+ { $$ = _gmx_selexpr_create_value_expr($1); }
+ | str_expr %prec PARAM_REDUCT
+ { $$ = _gmx_selexpr_create_value_expr($1); }
+ | value_item_range { $$ = $1; }
+;
+
+basic_value_item:
+ integer_number %prec PARAM_REDUCT
+ {
+ $$ = _gmx_selexpr_create_value(INT_VALUE);
+ $$->u.i.i1 = $$->u.i.i2 = $1;
+ }
+ | real_number %prec PARAM_REDUCT
+ {
+ $$ = _gmx_selexpr_create_value(REAL_VALUE);
+ $$->u.r.r1 = $$->u.r.r2 = $1;
+ }
+ | string %prec PARAM_REDUCT
+ {
+ $$ = _gmx_selexpr_create_value(STR_VALUE);
+ $$->u.s = $1;
+ }
+ | value_item_range { $$ = $1; }
+;
+
+value_item_range:
+ integer_number TO integer_number
+ {
+ $$ = _gmx_selexpr_create_value(INT_VALUE);
+ $$->u.i.i1 = $1; $$->u.i.i2 = $3;
+ }
+ | integer_number TO real_number
+ {
+ $$ = _gmx_selexpr_create_value(REAL_VALUE);
+ $$->u.r.r1 = $1; $$->u.r.r2 = $3;
+ }
+ | real_number TO number
+ {
+ $$ = _gmx_selexpr_create_value(REAL_VALUE);
+ $$->u.r.r1 = $1; $$->u.r.r2 = $3;
+ }
+;
+
+%%
+
+static t_selexpr_value *
+process_value_list(t_selexpr_value *values, int *nr)
+{
+ t_selexpr_value *val, *pval, *nval;
+
+ /* Count values (if needed) and reverse list */
+ if (nr)
+ {
+ *nr = 0;
+ }
+ pval = NULL;
+ val = values;
+ while (val)
+ {
+ if (nr)
+ {
+ ++*nr;
+ }
+ nval = val->next;
+ val->next = pval;
+ pval = val;
+ val = nval;
+ }
+ values = pval;
+
+ return values;
+}
+
+static t_selexpr_param *
+process_param_list(t_selexpr_param *params)
+{
+ t_selexpr_param *par, *ppar, *npar;
+
+ /* Reverse list */
+ ppar = NULL;
+ par = params;
+ while (par)
+ {
+ npar = par->next;
+ par->next = ppar;
+ ppar = par;
+ par = npar;
+ }
+ params = ppar;
+
+ return params;
+}
+
+static void
+yyerror(yyscan_t scanner, char const *s)
+{
+ _gmx_selparser_error(scanner, "%s", s);
+}
+
+
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in parsetree.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+/*! \internal
+ * \page page_module_selection_parser Selection parsing
+ *
+ * The selection parser is implemented in the following files:
+ * - scanner.l:
+ * Tokenizer implemented using Flex, splits the input into tokens
+ * (scanner.c and scanner_flex.h are generated from this file).
+ * - scanner.h, scanner_internal.h, scanner_internal.cpp:
+ * Helper functions for scanner.l and for interfacing between
+ * scanner.l and parser.y. Functions in scanner_internal.h are only
+ * used from scanner.l, while scanner.h is used from the parser.
+ * - symrec.h, symrec.cpp:
+ * Functions used by the tokenizer to handle the symbol table, i.e.,
+ * the recognized keywords. Some basic keywords are hardcoded into
+ * scanner.l, but all method and variable references go through the
+ * symbol table, as do position evaluation keywords.
+ * - parser.y:
+ * Semantic rules for parsing the grammar
+ * (parser.cpp and parser.h are generated from this file by Bison).
+ * - parsetree.h, parsetree.cpp:
+ * Functions called from actions in parser.y to construct the
+ * evaluation elements corresponding to different grammar elements.
+ * - params.c:
+ * Defines a function that processes the parameters of selection
+ * methods and initializes the children of the method element.
+ * - selectioncollection.h, selectioncollection.cpp:
+ * These files define the high-level public interface to the parser
+ * through SelectionCollection::parseFromStdin(),
+ * SelectionCollection::parseFromFile() and
+ * SelectionCollection::parseFromString().
+ *
+ * The basic control flow in the parser is as follows: when a parser function
+ * in SelectionCollection gets called, it performs some
+ * initialization, and then calls the _gmx_sel_yyparse() function generated
+ * by Bison. This function then calls _gmx_sel_yylex() to repeatedly read
+ * tokens from the input (more complex tasks related to token recognition
+ * and bookkeeping are done by functions in scanner_internal.cpp) and uses the
+ * grammar rules to decide what to do with them. Whenever a grammar rule
+ * matches, a corresponding function in parsetree.cpp is called to construct
+ * either a temporary representation for the object or a ::t_selelem object
+ * (some simple rules are handled internally in parser.y).
+ * When a complete selection has been parsed, the functions in parsetree.cpp
+ * also take care of updating the ::gmx_ana_selcollection_t structure
+ * appropriately.
+ *
+ * The rest of this page describes the resulting ::t_selelem object tree.
+ * Before the selections can be evaluated, this tree needs to be passed to
+ * the selection compiler, which is described on a separate page:
+ * \ref page_module_selection_compiler
+ *
+ *
+ * \section selparser_tree Element tree constructed by the parser
+ *
+ * The parser initializes the following fields in all selection elements:
+ * \c t_selelem::name, \c t_selelem::type, \c t_selelem::v\c .type,
+ * \c t_selelem::flags, \c t_selelem::child, \c t_selelem::next, and
+ * \c t_selelem::refcount.
+ * Some other fields are also initialized for particular element types as
+ * discussed below.
+ * Fields that are not initialized are set to zero, NULL, or other similar
+ * value.
+ *
+ *
+ * \subsection selparser_tree_root Root elements
+ *
+ * The parser creates a \ref SEL_ROOT selection element for each variable
+ * assignment and each selection. However, there are two exceptions that do
+ * not result in a \ref SEL_ROOT element (in these cases, only the symbol
+ * table is modified):
+ * - Variable assignments that assign a variable to another variable.
+ * - Variable assignments that assign a non-group constant.
+ * .
+ * The \ref SEL_ROOT elements are linked together in a chain in the same order
+ * as in the input.
+ *
+ * The children of the \ref SEL_ROOT elements can be used to distinguish
+ * the two types of root elements from each other:
+ * - For variable assignments, the first and only child is always
+ * a \ref SEL_SUBEXPR element.
+ * - For selections, the first child is a \ref SEL_EXPRESSION or a
+ * \ref SEL_MODIFIER element that evaluates the final positions (if the
+ * selection defines a constant position, the child is a \ref SEL_CONST).
+ * The rest of the children are \ref SEL_MODIFIER elements with
+ * \ref NO_VALUE, in the order given by the user.
+ * .
+ * The name of the selection/variable is stored in \c t_selelem::cgrp\c .name.
+ * It is set to either the name provided by the user or the selection string
+ * for selections not explicitly named by the user.
+ * \ref SEL_ROOT or \ref SEL_SUBEXPR elements do not appear anywhere else.
+ *
+ *
+ * \subsection selparser_tree_const Constant elements
+ *
+ * \ref SEL_CONST elements are created for every constant that is required
+ * for later evaluation.
+ * Currently, \ref SEL_CONST elements can be present for
+ * - selections that consist of a constant position,
+ * - \ref GROUP_VALUE method parameters if provided using external index
+ * groups,
+ * .
+ * For group-valued elements, the value is stored in \c t_selelem::cgrp;
+ * other types of values are stored in \c t_selelem::v.
+ * Constants that appear as parameters for selection methods are not present
+ * in the selection tree unless they have \ref GROUP_VALUE.
+ * \ref SEL_CONST elements have no children.
+ *
+ *
+ * \subsection selparser_tree_method Method evaluation elements
+ *
+ * \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements are treated very
+ * similarly. The \c gmx_ana_selmethod_t structure corresponding to the
+ * evaluation method is in \c t_selelem::method, and the method data in
+ * \c t_selelem::mdata has been allocated using sel_datafunc().
+ * If a non-standard reference position type was set, \c t_selelem::pc has
+ * also been created, but only the type has been set.
+ * All children of these elements are of the type \ref SEL_SUBEXPRREF, and
+ * each describes a selection that needs to be evaluated to obtain a value
+ * for one parameter of the method.
+ * No children are present for parameters that were given a constant
+ * non-\ref GROUP_VALUE value.
+ * The children are sorted in the order in which the parameters appear in the
+ * \ref gmx_ana_selmethod_t structure.
+ *
+ * In addition to actual selection keywords, \ref SEL_EXPRESSION elements
+ * are used internally to implement numerical comparisons (e.g., "x < 5")
+ * and keyword matching (e.g., "resnr 1 to 3" or "name CA").
+ *
+ *
+ * \subsection selparser_tree_subexpr Subexpression elements
+ *
+ * \ref SEL_SUBEXPR elements only appear for variables, as described above.
+ * \c t_selelem::name points to the name of the variable (from the
+ * \ref SEL_ROOT element).
+ * The element always has exactly one child, which represents the value of
+ * the variable.
+ * \ref SEL_SUBEXPR element is the only element type that can have
+ * \c t_selelem::refcount different from 1.
+ *
+ * \ref SEL_SUBEXPRREF elements are used for two purposes:
+ * - Variable references that need to be evaluated (i.e., there is a
+ * \ref SEL_SUBEXPR element for the variable) are represented using
+ * \ref SEL_SUBEXPRREF elements.
+ * In this case, \c t_selelem::param is NULL, and the first and only
+ * child of the element is the \ref SEL_SUBEXPR element of the variable.
+ * Such references can appear anywhere where the variable value
+ * (the child of the \ref SEL_SUBEXPR element) would be valid.
+ * - Children of \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements are
+ * always of this type. For these elements, \c t_selelem::param is
+ * initialized to point to the parameter that receives the value from
+ * the expression.
+ * Each such element has exactly one child, which can be of any type;
+ * the \ref SEL_SUBEXPR element of a variable is used if the value comes
+ * from a variable, otherwise the child type is not \ref SEL_SUBEXPR.
+ *
+ *
+ * \subsection selparser_tree_gmx_bool Boolean elements
+ *
+ * One \ref SEL_BOOLEAN element is created for each gmx_boolean keyword in the
+ * input, and the tree structure represents the evaluation order.
+ * The \c t_selelem::boolt type gives the type of the operation.
+ * Each element has exactly two children (one for \ref BOOL_NOT elements),
+ * which are in the order given in the input.
+ * The children always have \ref GROUP_VALUE, but different element types
+ * are possible.
+ *
+ *
+ * \subsection selparser_tree_arith Arithmetic elements
+ *
+ * One \ref SEL_ARITHMETIC element is created for each arithmetic operation in
+ * the input, and the tree structure represents the evaluation order.
+ * The \c t_selelem::optype type gives the name of the operation.
+ * Each element has exactly two children (one for unary negation elements),
+ * which are in the order given in the input.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <futil.h>
+#include <smalloc.h>
+#include <string2.h>
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/errorreporting/errorcontext.h"
+#include "gromacs/fatalerror/fatalerror.h"
+
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "keywords.h"
+#include "parsetree.h"
+#include "selectioncollection-impl.h"
+#include "selelem.h"
+#include "selhelp.h"
+#include "symrec.h"
+
+#include "scanner.h"
+
+void
+_gmx_selparser_warning(yyscan_t scanner, const char *fmt, ...)
+{
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[1024];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+ errors->warning(buf);
+}
+
+void
+_gmx_selparser_error(yyscan_t scanner, const char *fmt, ...)
+{
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[1024];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+ errors->error(buf);
+}
+
+/*!
+ * \param[in] type Type for the new value.
+ * \returns Pointer to the newly allocated value.
+ */
+t_selexpr_value *
+_gmx_selexpr_create_value(e_selvalue_t type)
+{
+ t_selexpr_value *value;
+ snew(value, 1);
+ value->type = type;
+ value->bExpr = FALSE;
+ value->next = NULL;
+ return value;
+}
+
+/*!
+ * \param[in] expr Expression for the value.
+ * \returns Pointer to the newly allocated value.
+ */
+t_selexpr_value *
+_gmx_selexpr_create_value_expr(t_selelem *expr)
+{
+ t_selexpr_value *value;
+ snew(value, 1);
+ value->type = expr->v.type;
+ value->bExpr = TRUE;
+ value->u.expr = expr;
+ value->next = NULL;
+ return value;
+}
+
+/*!
+ * \param[in] name Name for the new parameter.
+ * \returns Pointer to the newly allocated parameter.
+ *
+ * No copy of \p name is made.
+ */
+t_selexpr_param *
+_gmx_selexpr_create_param(char *name)
+{
+ t_selexpr_param *param;
+ snew(param, 1);
+ param->name = name;
+ param->next = NULL;
+ return param;
+}
+
+/*!
+ * \param value Pointer to the beginning of the value list to free.
+ *
+ * The expressions referenced by the values are also freed
+ * (to prevent this, set the expression to NULL before calling the function).
+ */
+void
+_gmx_selexpr_free_values(t_selexpr_value *value)
+{
+ t_selexpr_value *old;
+
+ while (value)
+ {
+ if (value->bExpr)
+ {
+ if (value->u.expr)
+ {
+ _gmx_selelem_free(value->u.expr);
+ }
+ }
+ else if (value->type == STR_VALUE)
+ {
+ sfree(value->u.s);
+ }
+ old = value;
+ value = value->next;
+ sfree(old);
+ }
+}
+
+/*!
+ * \param param Pointer the the beginning of the parameter list to free.
+ *
+ * The values of the parameters are freed with free_selexpr_values().
+ */
+void
+_gmx_selexpr_free_params(t_selexpr_param *param)
+{
+ t_selexpr_param *old;
+
+ while (param)
+ {
+ _gmx_selexpr_free_values(param->value);
+ old = param;
+ param = param->next;
+ sfree(old->name);
+ sfree(old);
+ }
+}
+
+/*!
+ * \param[in,out] sel Root of the selection element tree to initialize.
+ * \param[in] scanner Scanner data structure.
+ * \returns 0 on success, an error code on error.
+ *
+ * Propagates the \ref SEL_DYNAMIC flag from the children of \p sel to \p sel
+ * (if any child of \p sel is dynamic, \p sel is also marked as such).
+ * The \ref SEL_DYNAMIC flag is also set for \ref SEL_EXPRESSION elements with
+ * a dynamic method.
+ * Also, sets one of the \ref SEL_SINGLEVAL, \ref SEL_ATOMVAL, or
+ * \ref SEL_VARNUMVAL flags, either based on the children or on the type of
+ * the selection method.
+ * If the types of the children conflict, an error is returned.
+ *
+ * The flags of the children of \p sel are also updated if not done earlier.
+ * The flags are initialized only once for any element; if \ref SEL_FLAGSSET
+ * is set for an element, the function returns immediately, and the recursive
+ * operation does not descend beyond such elements.
+ */
+int
+_gmx_selelem_update_flags(t_selelem *sel, yyscan_t scanner)
+{
+ t_selelem *child;
+ int rc;
+ gmx_bool bUseChildType=FALSE;
+ gmx_bool bOnlySingleChildren;
+
+ /* Return if the flags have already been set */
+ if (sel->flags & SEL_FLAGSSET)
+ {
+ return 0;
+ }
+ /* Set the flags based on the current element type */
+ switch (sel->type)
+ {
+ case SEL_CONST:
+ case SEL_GROUPREF:
+ sel->flags |= SEL_SINGLEVAL;
+ bUseChildType = FALSE;
+ break;
+
+ case SEL_EXPRESSION:
+ if (sel->u.expr.method->flags & SMETH_DYNAMIC)
+ {
+ sel->flags |= SEL_DYNAMIC;
+ }
+ if (sel->u.expr.method->flags & SMETH_SINGLEVAL)
+ {
+ sel->flags |= SEL_SINGLEVAL;
+ }
+ else if (sel->u.expr.method->flags & SMETH_VARNUMVAL)
+ {
+ sel->flags |= SEL_VARNUMVAL;
+ }
+ else
+ {
+ sel->flags |= SEL_ATOMVAL;
+ }
+ bUseChildType = FALSE;
+ break;
+
+ case SEL_ARITHMETIC:
+ sel->flags |= SEL_ATOMVAL;
+ bUseChildType = FALSE;
+ break;
+
+ case SEL_MODIFIER:
+ if (sel->v.type != NO_VALUE)
+ {
+ sel->flags |= SEL_VARNUMVAL;
+ }
+ bUseChildType = FALSE;
+ break;
+
+ case SEL_ROOT:
+ bUseChildType = FALSE;
+ break;
+
+ case SEL_BOOLEAN:
+ case SEL_SUBEXPR:
+ case SEL_SUBEXPRREF:
+ bUseChildType = TRUE;
+ break;
+ }
+ /* Loop through children to propagate their flags upwards */
+ bOnlySingleChildren = TRUE;
+ child = sel->child;
+ while (child)
+ {
+ /* Update the child */
+ rc = _gmx_selelem_update_flags(child, scanner);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ /* Propagate the dynamic flag */
+ sel->flags |= (child->flags & SEL_DYNAMIC);
+ /* Propagate the type flag if necessary and check for problems */
+ if (bUseChildType)
+ {
+ if ((sel->flags & SEL_VALTYPEMASK)
+ && !(sel->flags & child->flags & SEL_VALTYPEMASK))
+ {
+ _gmx_selparser_error(scanner, "invalid combination of selection expressions");
+ return gmx::eeInvalidInput;
+ }
+ sel->flags |= (child->flags & SEL_VALTYPEMASK);
+ }
+ if (!(child->flags & SEL_SINGLEVAL))
+ {
+ bOnlySingleChildren = FALSE;
+ }
+
+ child = child->next;
+ }
+ /* For arithmetic expressions consisting only of single values,
+ * the result is also a single value. */
+ if (sel->type == SEL_ARITHMETIC && bOnlySingleChildren)
+ {
+ sel->flags = (sel->flags & ~SEL_VALTYPEMASK) | SEL_SINGLEVAL;
+ }
+ /* For root elements, the type should be propagated here, after the
+ * children have been updated. */
+ if (sel->type == SEL_ROOT)
+ {
+ sel->flags |= (sel->child->flags & SEL_VALTYPEMASK);
+ }
+ /* Mark that the flags are set */
+ sel->flags |= SEL_FLAGSSET;
+ return 0;
+}
+
+/*!
+ * \param[in,out] sel Selection element to initialize.
+ * \param[in] scanner Scanner data structure.
+ *
+ * A deep copy of the parameters is made to allow several
+ * expressions with the same method to coexist peacefully.
+ * Calls sel_datafunc() if one is specified for the method.
+ */
+void
+_gmx_selelem_init_method_params(t_selelem *sel, yyscan_t scanner)
+{
+ int nparams;
+ gmx_ana_selparam_t *orgparam;
+ gmx_ana_selparam_t *param;
+ int i;
+ void *mdata;
+
+ nparams = sel->u.expr.method->nparams;
+ orgparam = sel->u.expr.method->param;
+ snew(param, nparams);
+ memcpy(param, orgparam, nparams*sizeof(gmx_ana_selparam_t));
+ for (i = 0; i < nparams; ++i)
+ {
+ param[i].flags &= ~SPAR_SET;
+ _gmx_selvalue_setstore(¶m[i].val, NULL);
+ if (param[i].flags & SPAR_VARNUM)
+ {
+ param[i].val.nr = -1;
+ }
+ /* Duplicate the enum value array if it is given statically */
+ if ((param[i].flags & SPAR_ENUMVAL) && orgparam[i].val.u.ptr != NULL)
+ {
+ int n;
+
+ /* Count the values */
+ n = 1;
+ while (orgparam[i].val.u.s[n] != NULL)
+ {
+ ++n;
+ }
+ _gmx_selvalue_reserve(¶m[i].val, n+1);
+ memcpy(param[i].val.u.s, orgparam[i].val.u.s,
+ (n+1)*sizeof(param[i].val.u.s[0]));
+ }
+ }
+ mdata = NULL;
+ if (sel->u.expr.method->init_data)
+ {
+ mdata = sel->u.expr.method->init_data(nparams, param);
+ if (mdata == NULL)
+ {
+ GMX_ERROR_NORET(gmx::eeInvalidValue, "Method data initialization failed");
+ }
+ }
+ if (sel->u.expr.method->set_poscoll)
+ {
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+
+ sel->u.expr.method->set_poscoll(sc->pcc, mdata);
+ }
+ /* Store the values */
+ sel->u.expr.method->param = param;
+ sel->u.expr.mdata = mdata;
+}
+
+/*!
+ * \param[in,out] sel Selection element to initialize.
+ * \param[in] method Selection method to set.
+ * \param[in] scanner Scanner data structure.
+ *
+ * Makes a copy of \p method and stores it in \p sel->u.expr.method,
+ * and calls _gmx_selelem_init_method_params();
+ */
+void
+_gmx_selelem_set_method(t_selelem *sel, gmx_ana_selmethod_t *method,
+ yyscan_t scanner)
+{
+ int i;
+
+ _gmx_selelem_set_vtype(sel, method->type);
+ sel->name = method->name;
+ snew(sel->u.expr.method, 1);
+ memcpy(sel->u.expr.method, method, sizeof(gmx_ana_selmethod_t));
+ _gmx_selelem_init_method_params(sel, scanner);
+}
+
+/*! \brief
+ * Initializes the reference position calculation for a \ref SEL_EXPRESSION
+ * element.
+ *
+ * \param[in,out] pcc Position calculation collection to use.
+ * \param[in,out] sel Selection element to initialize.
+ * \param[in] rpost Reference position type to use (NULL = default).
+ * \param[in] scanner Scanner data structure.
+ * \returns 0 on success, a non-zero error code on error.
+ */
+static int
+set_refpos_type(gmx_ana_poscalc_coll_t *pcc, t_selelem *sel, const char *rpost,
+ yyscan_t scanner)
+{
+ int rc;
+
+ if (!rpost)
+ {
+ return 0;
+ }
+
+ rc = 0;
+ if (sel->u.expr.method->pupdate)
+ {
+ /* By default, use whole residues/molecules. */
+ rc = gmx_ana_poscalc_create_enum(&sel->u.expr.pc, pcc, rpost,
+ POS_COMPLWHOLE);
+ }
+ else
+ {
+ _gmx_selparser_warning(scanner, "modifier '%s' for '%s' ignored",
+ rpost, sel->u.expr.method->name);
+ }
+ return rc;
+}
+
+/*!
+ * \param[in] left Selection element for the left hand side.
+ * \param[in] right Selection element for the right hand side.
+ * \param[in] op String representation of the operator.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created selection element.
+ *
+ * This function handles the creation of a \c t_selelem object for
+ * arithmetic expressions.
+ */
+t_selelem *
+_gmx_sel_init_arithmetic(t_selelem *left, t_selelem *right, char op,
+ yyscan_t scanner)
+{
+ t_selelem *sel;
+ char buf[2];
+
+ buf[0] = op;
+ buf[1] = 0;
+ sel = _gmx_selelem_create(SEL_ARITHMETIC);
+ sel->v.type = REAL_VALUE;
+ switch(op)
+ {
+ case '+': sel->u.arith.type = ARITH_PLUS; break;
+ case '-': sel->u.arith.type = (right ? ARITH_MINUS : ARITH_NEG); break;
+ case '*': sel->u.arith.type = ARITH_MULT; break;
+ case '/': sel->u.arith.type = ARITH_DIV; break;
+ case '^': sel->u.arith.type = ARITH_EXP; break;
+ }
+ sel->u.arith.opstr = strdup(buf);
+ sel->name = sel->u.arith.opstr;
+ sel->child = left;
+ sel->child->next = right;
+ return sel;
+}
+
+/*!
+ * \param[in] left Selection element for the left hand side.
+ * \param[in] right Selection element for the right hand side.
+ * \param[in] cmpop String representation of the comparison operator.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created selection element.
+ *
+ * This function handles the creation of a \c t_selelem object for
+ * comparison expressions.
+ */
+t_selelem *
+_gmx_sel_init_comparison(t_selelem *left, t_selelem *right, char *cmpop,
+ yyscan_t scanner)
+{
+ t_selelem *sel;
+ t_selexpr_param *params, *param;
+ const char *name;
+ int rc;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ gmx::ErrorContext context(errors, "In comparison initialization");
+
+ sel = _gmx_selelem_create(SEL_EXPRESSION);
+ _gmx_selelem_set_method(sel, &sm_compare, scanner);
+ /* Create the parameter for the left expression */
+ name = left->v.type == INT_VALUE ? "int1" : "real1";
+ params = param = _gmx_selexpr_create_param(strdup(name));
+ param->nval = 1;
+ param->value = _gmx_selexpr_create_value_expr(left);
+ /* Create the parameter for the right expression */
+ name = right->v.type == INT_VALUE ? "int2" : "real2";
+ param = _gmx_selexpr_create_param(strdup(name));
+ param->nval = 1;
+ param->value = _gmx_selexpr_create_value_expr(right);
+ params->next = param;
+ /* Create the parameter for the operator */
+ param = _gmx_selexpr_create_param(strdup("op"));
+ param->nval = 1;
+ param->value = _gmx_selexpr_create_value(STR_VALUE);
+ param->value->u.s = cmpop;
+ params->next->next = param;
+ if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams,
+ sel->u.expr.method->param, sel, scanner))
+ {
+ _gmx_selelem_free(sel);
+ return NULL;
+ }
+
+ return sel;
+}
+
+/*!
+ * \param[in] method Method to use.
+ * \param[in] args Pointer to the first argument.
+ * \param[in] rpost Reference position type to use (NULL = default).
+ * \param[in] scanner Scanner data structure.
+ * \returns The created selection element.
+ *
+ * This function handles the creation of a \c t_selelem object for
+ * selection methods that do not take parameters.
+ */
+t_selelem *
+_gmx_sel_init_keyword(gmx_ana_selmethod_t *method, t_selexpr_value *args,
+ const char *rpost, yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ t_selelem *root, *child;
+ t_selexpr_param *params, *param;
+ t_selexpr_value *arg;
+ int nargs;
+ int rc;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[128];
+ sprintf(buf, "In keyword '%s'", method->name);
+ gmx::ErrorContext context(errors, buf);
+
+ if (method->nparams > 0)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Keyword initialization called with non-keyword method");
+ return NULL;
+ }
+
+ root = _gmx_selelem_create(SEL_EXPRESSION);
+ child = root;
+ _gmx_selelem_set_method(child, method, scanner);
+
+ /* Initialize the evaluation of keyword matching if values are provided */
+ if (args)
+ {
+ gmx_ana_selmethod_t *kwmethod;
+ switch (method->type)
+ {
+ case INT_VALUE: kwmethod = &sm_keyword_int; break;
+ case REAL_VALUE: kwmethod = &sm_keyword_real; break;
+ case STR_VALUE: kwmethod = &sm_keyword_str; break;
+ default:
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Unknown type for keyword selection");
+ _gmx_selexpr_free_values(args);
+ goto on_error;
+ }
+ /* Count the arguments */
+ nargs = 0;
+ arg = args;
+ while (arg)
+ {
+ ++nargs;
+ arg = arg->next;
+ }
+ /* Initialize the selection element */
+ root = _gmx_selelem_create(SEL_EXPRESSION);
+ _gmx_selelem_set_method(root, kwmethod, scanner);
+ params = param = _gmx_selexpr_create_param(NULL);
+ param->nval = 1;
+ param->value = _gmx_selexpr_create_value_expr(child);
+ param = _gmx_selexpr_create_param(NULL);
+ param->nval = nargs;
+ param->value = args;
+ params->next = param;
+ if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
+ root->u.expr.method->param, root, scanner))
+ {
+ goto on_error;
+ }
+ }
+ rc = set_refpos_type(sc->pcc, child, rpost, scanner);
+ if (rc != 0)
+ {
+ goto on_error;
+ }
+
+ return root;
+
+/* On error, free all memory and return NULL. */
+on_error:
+ _gmx_selelem_free(root);
+ return NULL;
+}
+
+/*!
+ * \param[in] method Method to use for initialization.
+ * \param[in] params Pointer to the first parameter.
+ * \param[in] rpost Reference position type to use (NULL = default).
+ * \param[in] scanner Scanner data structure.
+ * \returns The created selection element.
+ *
+ * This function handles the creation of a \c t_selelem object for
+ * selection methods that take parameters.
+ *
+ * Part of the behavior of the \c same selection keyword is hardcoded into
+ * this function (or rather, into _gmx_selelem_custom_init_same()) to allow the
+ * use of any keyword in \c "same KEYWORD as" without requiring special
+ * handling somewhere else (or sacrificing the simple syntax).
+ */
+t_selelem *
+_gmx_sel_init_method(gmx_ana_selmethod_t *method, t_selexpr_param *params,
+ const char *rpost, yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ t_selelem *root;
+ int rc;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[128];
+ sprintf(buf, "In keyword '%s'", method->name);
+ gmx::ErrorContext context(errors, buf);
+
+ _gmx_sel_finish_method(scanner);
+ /* The "same" keyword needs some custom massaging of the parameters. */
+ rc = _gmx_selelem_custom_init_same(&method, params, scanner);
+ if (rc != 0)
+ {
+ _gmx_selexpr_free_params(params);
+ return NULL;
+ }
+ root = _gmx_selelem_create(SEL_EXPRESSION);
+ _gmx_selelem_set_method(root, method, scanner);
+ /* Process the parameters */
+ if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
+ root->u.expr.method->param, root, scanner))
+ {
+ _gmx_selelem_free(root);
+ return NULL;
+ }
+ rc = set_refpos_type(sc->pcc, root, rpost, scanner);
+ if (rc != 0)
+ {
+ _gmx_selelem_free(root);
+ return NULL;
+ }
+
+ return root;
+}
+
+/*!
+ * \param[in] method Modifier to use for initialization.
+ * \param[in] params Pointer to the first parameter.
+ * \param[in] sel Selection element that the modifier should act on.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created selection element.
+ *
+ * This function handles the creation of a \c t_selelem object for
+ * selection modifiers.
+ */
+t_selelem *
+_gmx_sel_init_modifier(gmx_ana_selmethod_t *method, t_selexpr_param *params,
+ t_selelem *sel, yyscan_t scanner)
+{
+ t_selelem *root;
+ t_selelem *mod;
+ t_selexpr_param *vparam;
+ int i;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[128];
+ sprintf(buf, "In keyword '%s'", method->name);
+ gmx::ErrorContext context(errors, buf);
+
+ _gmx_sel_finish_method(scanner);
+ mod = _gmx_selelem_create(SEL_MODIFIER);
+ _gmx_selelem_set_method(mod, method, scanner);
+ if (method->type == NO_VALUE)
+ {
+ t_selelem *child;
+
+ child = sel;
+ while (child->next)
+ {
+ child = child->next;
+ }
+ child->next = mod;
+ root = sel;
+ }
+ else
+ {
+ vparam = _gmx_selexpr_create_param(NULL);
+ vparam->nval = 1;
+ vparam->value = _gmx_selexpr_create_value_expr(sel);
+ vparam->next = params;
+ params = vparam;
+ root = mod;
+ }
+ /* Process the parameters */
+ if (!_gmx_sel_parse_params(params, mod->u.expr.method->nparams,
+ mod->u.expr.method->param, mod, scanner))
+ {
+ _gmx_selelem_free(mod);
+ return NULL;
+ }
+
+ return root;
+}
+
+/*!
+ * \param[in] expr Input selection element for the position calculation.
+ * \param[in] type Reference position type or NULL for default.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created selection element.
+ *
+ * This function handles the creation of a \c t_selelem object for
+ * evaluation of reference positions.
+ */
+t_selelem *
+_gmx_sel_init_position(t_selelem *expr, const char *type, yyscan_t scanner)
+{
+ t_selelem *root;
+ t_selexpr_param *params;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[128];
+ sprintf(buf, "In position evaluation");
+ gmx::ErrorContext context(errors, buf);
+
+ root = _gmx_selelem_create(SEL_EXPRESSION);
+ _gmx_selelem_set_method(root, &sm_keyword_pos, scanner);
+ _gmx_selelem_set_kwpos_type(root, type);
+ /* Create the parameters for the parameter parser. */
+ params = _gmx_selexpr_create_param(NULL);
+ params->nval = 1;
+ params->value = _gmx_selexpr_create_value_expr(expr);
+ /* Parse the parameters. */
+ if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
+ root->u.expr.method->param, root, scanner))
+ {
+ _gmx_selelem_free(root);
+ return NULL;
+ }
+
+ return root;
+}
+
+/*!
+ * \param[in] x,y,z Coordinates for the position.
+ * \returns The creates selection element.
+ */
+t_selelem *
+_gmx_sel_init_const_position(real x, real y, real z)
+{
+ t_selelem *sel;
+ rvec pos;
+
+ sel = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype(sel, POS_VALUE);
+ _gmx_selvalue_reserve(&sel->v, 1);
+ pos[XX] = x;
+ pos[YY] = y;
+ pos[ZZ] = z;
+ gmx_ana_pos_init_const(sel->v.u.p, pos);
+ return sel;
+}
+
+/*!
+ * \param[in] name Name of an index group to search for.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created constant selection element, or NULL if no matching
+ * index group found.
+ *
+ * See gmx_ana_indexgrps_find() for information on how \p name is matched
+ * against the index groups.
+ */
+t_selelem *
+_gmx_sel_init_group_by_name(const char *name, yyscan_t scanner)
+{
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[256];
+ gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
+ t_selelem *sel;
+
+ if (!_gmx_sel_lexer_has_groups_set(scanner))
+ {
+ sel = _gmx_selelem_create(SEL_GROUPREF);
+ _gmx_selelem_set_vtype(sel, GROUP_VALUE);
+ sel->u.gref.name = strdup(name);
+ sel->u.gref.id = -1;
+ sel->name = name;
+ return sel;
+ }
+ if (!grps)
+ {
+ sprintf(buf, "No index groups set; cannot match 'group %s'", name);
+ errors->error(buf);
+ return NULL;
+ }
+ sel = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype(sel, GROUP_VALUE);
+ /* FIXME: The constness should not be cast away */
+ if (!gmx_ana_indexgrps_find(&sel->u.cgrp, grps, (char *)name))
+ {
+ sprintf(buf, "Cannot match 'group %s'", name);
+ errors->error(buf);
+ _gmx_selelem_free(sel);
+ return NULL;
+ }
+ sel->name = sel->u.cgrp.name;
+ return sel;
+}
+
+/*!
+ * \param[in] id Zero-based index number of the group to extract.
+ * \param[in] scanner Scanner data structure.
+ * \returns The created constant selection element, or NULL if no matching
+ * index group found.
+ */
+t_selelem *
+_gmx_sel_init_group_by_id(int id, yyscan_t scanner)
+{
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[128];
+ gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
+ t_selelem *sel;
+
+ if (!_gmx_sel_lexer_has_groups_set(scanner))
+ {
+ sel = _gmx_selelem_create(SEL_GROUPREF);
+ _gmx_selelem_set_vtype(sel, GROUP_VALUE);
+ sel->u.gref.name = NULL;
+ sel->u.gref.id = id;
+ return sel;
+ }
+ if (!grps)
+ {
+ sprintf(buf, "No index groups set; cannot match 'group %d'", id);
+ errors->error(buf);
+ return NULL;
+ }
+ sel = _gmx_selelem_create(SEL_CONST);
+ _gmx_selelem_set_vtype(sel, GROUP_VALUE);
+ if (!gmx_ana_indexgrps_extract(&sel->u.cgrp, grps, id))
+ {
+ sprintf(buf, "Cannot match 'group %d'", id);
+ errors->error(buf);
+ _gmx_selelem_free(sel);
+ return NULL;
+ }
+ sel->name = sel->u.cgrp.name;
+ return sel;
+}
+
+/*!
+ * \param[in,out] sel Value of the variable.
+ * \returns The created selection element that references \p sel.
+ *
+ * The reference count of \p sel is updated, but no other modifications are
+ * made.
+ */
+t_selelem *
+_gmx_sel_init_variable_ref(t_selelem *sel)
+{
+ t_selelem *ref;
+
+ if (sel->v.type == POS_VALUE && sel->type == SEL_CONST)
+ {
+ ref = sel;
+ }
+ else
+ {
+ ref = _gmx_selelem_create(SEL_SUBEXPRREF);
+ _gmx_selelem_set_vtype(ref, sel->v.type);
+ ref->name = sel->name;
+ ref->child = sel;
+ }
+ sel->refcount++;
+ return ref;
+}
+
+/*!
+ * \param[in] name Name for the selection
+ * (if NULL, a default name is constructed).
+ * \param[in] sel The selection element that evaluates the selection.
+ * \param scanner Scanner data structure.
+ * \returns The created root selection element.
+ *
+ * This function handles the creation of root (\ref SEL_ROOT) \c t_selelem
+ * objects for selections.
+ */
+t_selelem *
+_gmx_sel_init_selection(char *name, t_selelem *sel, yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ t_selelem *root;
+ int rc;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[1024];
+ sprintf(buf, "In selection '%s'", _gmx_sel_lexer_pselstr(scanner));
+ gmx::ErrorContext context(errors, buf);
+
+ if (sel->v.type != POS_VALUE)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Each selection must evaluate to a position");
+ /* FIXME: Better handling of this error */
+ sfree(name);
+ return NULL;
+ }
+
+ root = _gmx_selelem_create(SEL_ROOT);
+ root->child = sel;
+ /* Assign the name (this is done here to free it automatically in the case
+ * of an error below). */
+ if (name)
+ {
+ root->name = root->u.cgrp.name = name;
+ }
+ /* Update the flags */
+ rc = _gmx_selelem_update_flags(root, scanner);
+ if (rc != 0)
+ {
+ _gmx_selelem_free(root);
+ return NULL;
+ }
+
+ /* If there is no name provided by the user, check whether the actual
+ * selection given was from an external group, and if so, use the name
+ * of the external group. */
+ if (!root->name)
+ {
+ t_selelem *child = root->child;
+ while (child->type == SEL_MODIFIER)
+ {
+ if (!child->child || child->child->type != SEL_SUBEXPRREF
+ || !child->child->child)
+ {
+ break;
+ }
+ child = child->child->child;
+ }
+ if (child->type == SEL_EXPRESSION
+ && child->child && child->child->type == SEL_SUBEXPRREF
+ && child->child->child
+ && child->child->child->type == SEL_CONST
+ && child->child->child->v.type == GROUP_VALUE)
+ {
+ root->name = root->u.cgrp.name =
+ strdup(child->child->child->u.cgrp.name);
+ }
+ }
+ /* If there still is no name, use the selection string */
+ if (!root->name)
+ {
+ root->name = root->u.cgrp.name
+ = strdup(_gmx_sel_lexer_pselstr(scanner));
+ }
+
+ /* Print out some information if the parser is interactive */
+ if (_gmx_sel_is_lexer_interactive(scanner))
+ {
+ fprintf(stderr, "Selection '%s' parsed\n",
+ _gmx_sel_lexer_pselstr(scanner));
+ }
+
+ return root;
+}
+
+
+/*!
+ * \param[in] name Name of the variable (should not be freed after this
+ * function).
+ * \param[in] expr The selection element that evaluates the variable.
+ * \param scanner Scanner data structure.
+ * \returns The created root selection element.
+ *
+ * This function handles the creation of root \c t_selelem objects for
+ * variable assignments. A \ref SEL_ROOT element and a \ref SEL_SUBEXPR
+ * element are both created.
+ */
+t_selelem *
+_gmx_sel_assign_variable(char *name, t_selelem *expr, yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ const char *pselstr = _gmx_sel_lexer_pselstr(scanner);
+ t_selelem *root = NULL;
+ int rc;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[1024];
+ sprintf(buf, "In selection '%s'", pselstr);
+ gmx::ErrorContext context(errors, buf);
+
+ rc = _gmx_selelem_update_flags(expr, scanner);
+ if (rc != 0)
+ {
+ sfree(name);
+ _gmx_selelem_free(expr);
+ return NULL;
+ }
+ /* Check if this is a constant non-group value */
+ if (expr->type == SEL_CONST && expr->v.type != GROUP_VALUE)
+ {
+ /* If so, just assign the constant value to the variable */
+ if (!_gmx_sel_add_var_symbol(sc->symtab, name, expr))
+ {
+ _gmx_selelem_free(expr);
+ sfree(name);
+ return NULL;
+ }
+ _gmx_selelem_free(expr);
+ sfree(name);
+ goto finish;
+ }
+ /* Check if we are assigning a variable to another variable */
+ if (expr->type == SEL_SUBEXPRREF)
+ {
+ /* If so, make a simple alias */
+ if (!_gmx_sel_add_var_symbol(sc->symtab, name, expr->child))
+ {
+ _gmx_selelem_free(expr);
+ sfree(name);
+ return NULL;
+ }
+ _gmx_selelem_free(expr);
+ sfree(name);
+ goto finish;
+ }
+ /* Create the root element */
+ root = _gmx_selelem_create(SEL_ROOT);
+ root->name = name;
+ root->u.cgrp.name = name;
+ /* Create the subexpression element */
+ root->child = _gmx_selelem_create(SEL_SUBEXPR);
+ _gmx_selelem_set_vtype(root->child, expr->v.type);
+ root->child->name = name;
+ root->child->child = expr;
+ /* Update flags */
+ rc = _gmx_selelem_update_flags(root, scanner);
+ if (rc != 0)
+ {
+ _gmx_selelem_free(root);
+ return NULL;
+ }
+ /* Add the variable to the symbol table */
+ if (!_gmx_sel_add_var_symbol(sc->symtab, name, root->child))
+ {
+ _gmx_selelem_free(root);
+ return NULL;
+ }
+finish:
+ srenew(sc->varstrs, sc->nvars + 1);
+ sc->varstrs[sc->nvars] = strdup(pselstr);
+ ++sc->nvars;
+ if (_gmx_sel_is_lexer_interactive(scanner))
+ {
+ fprintf(stderr, "Variable '%s' parsed\n", pselstr);
+ }
+ return root;
+}
+
+/*!
+ * \param sel Selection to append (can be NULL, in which
+ * case nothing is done).
+ * \param last Last selection, or NULL if not present or not known.
+ * \param scanner Scanner data structure.
+ * \returns The last selection after the append.
+ *
+ * Appends \p sel after the last root element, and returns either \p sel
+ * (if it was non-NULL) or the last element (if \p sel was NULL).
+ */
+t_selelem *
+_gmx_sel_append_selection(t_selelem *sel, t_selelem *last, yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+
+ /* Append sel after last, or the last element of sc if last is NULL */
+ if (last)
+ {
+ last->next = sel;
+ }
+ else
+ {
+ if (sc->root)
+ {
+ last = sc->root;
+ while (last->next)
+ {
+ last = last->next;
+ }
+ last->next = sel;
+ }
+ else
+ {
+ sc->root = sel;
+ }
+ }
+ /* Initialize a selection object if necessary */
+ if (sel)
+ {
+ last = sel;
+ /* Add the new selection to the collection if it is not a variable. */
+ if (sel->child->type != SEL_SUBEXPR)
+ {
+ gmx::Selection *newsel
+ = new gmx::Selection(sel, _gmx_sel_lexer_pselstr(scanner));
+ sc->sel.push_back(newsel);
+ }
+ }
+ /* Clear the selection string now that we've saved it */
+ _gmx_sel_lexer_clear_pselstr(scanner);
+ return last;
+}
+
+/*!
+ * \param[in] scanner Scanner data structure.
+ * \returns TRUE if the parser should finish, FALSE if parsing should
+ * continue.
+ *
+ * This function is called always after _gmx_sel_append_selection() to
+ * check whether a sufficient number of selections has already been provided.
+ * This is used to terminate interactive parsers when the correct number of
+ * selections has been provided.
+ */
+gmx_bool
+_gmx_sel_parser_should_finish(yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ return (int)sc->sel.size() == _gmx_sel_lexer_exp_selcount(scanner);
+}
+
+/*!
+ * \param[in] scanner Scanner data structure.
+ */
+void
+_gmx_sel_handle_empty_cmd(yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
+ int i;
+
+ if (!_gmx_sel_is_lexer_interactive(scanner))
+ return;
+
+ if (grps)
+ {
+ fprintf(stderr, "Available index groups:\n");
+ gmx_ana_indexgrps_print(_gmx_sel_lexer_indexgrps(scanner), 0);
+ }
+ if (sc->nvars > 0 || !sc->sel.empty())
+ {
+ fprintf(stderr, "Currently provided selections:\n");
+ for (i = 0; i < sc->nvars; ++i)
+ {
+ fprintf(stderr, " %s\n", sc->varstrs[i]);
+ }
+ for (i = 0; i < (int)sc->sel.size(); ++i)
+ {
+ fprintf(stderr, " %2d. %s\n", i+1, sc->sel[i]->_sel.selstr);
+ }
+ }
+}
+
+/*!
+ * \param[in] topic Topic for which help was requested, or NULL for general
+ * help.
+ * \param[in] scanner Scanner data structure.
+ *
+ * \p topic is freed by this function.
+ */
+void
+_gmx_sel_handle_help_cmd(char *topic, yyscan_t scanner)
+{
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+
+ _gmx_sel_print_help(sc->symtab, topic);
+ if (topic)
+ {
+ sfree(topic);
+ }
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Handling of intermediate selection parser data.
+ *
+ * The data types declared in this header are used by the parser to store
+ * intermediate data when constructing method expressions.
+ * In particular, the parameters for the method are stored.
+ * The intermediate data is freed once a \c t_selelem object can be
+ * constructed.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_PARSETREE_H
+#define SELECTION_PARSETREE_H
+
+#include <types/simple.h>
+
+#include "gromacs/selection/selvalue.h"
+
+struct t_selelem;
+struct gmx_ana_indexgrps_t;
+struct gmx_ana_selmethod_t;
+struct gmx_ana_selparam_t;
+
+/*! \internal \brief
+ * Describes a parsed value, possibly resulting from expression evaluation.
+ */
+typedef struct t_selexpr_value
+{
+ /** Type of the value. */
+ e_selvalue_t type;
+ /** TRUE if the value is the result of an expression. */
+ gmx_bool bExpr;
+ /** The actual value. */
+ union {
+ /** The integer value/range (\p type INT_VALUE); */
+ struct {
+ /** Beginning of the range. */
+ int i1;
+ /** End of the range; equals \p i1 for a single integer. */
+ int i2;
+ } i;
+ /** The real value/range (\p type REAL_VALUE); */
+ struct {
+ /** Beginning of the range. */
+ real r1;
+ /** End of the range; equals \p r1 for a single number. */
+ real r2;
+ } r;
+ /** The string value (\p type STR_VALUE); */
+ char *s;
+ /** The position value (\p type POS_VALUE); */
+ rvec x;
+ /** The expression if \p bExpr is TRUE. */
+ struct t_selelem *expr;
+ } u;
+ /** Pointer to the next value. */
+ struct t_selexpr_value *next;
+} t_selexpr_value;
+
+/*! \internal \brief
+ * Describes a parsed method parameter.
+ */
+typedef struct t_selexpr_param
+{
+ /** Name of the parameter. */
+ char *name;
+ /** Number of values given for this parameter. */
+ int nval;
+ /** Pointer to the first value. */
+ struct t_selexpr_value *value;
+ /** Pointer to the next parameter. */
+ struct t_selexpr_param *next;
+} t_selexpr_param;
+
+/** Error reporting function for the selection parser. */
+void
+_gmx_selparser_warning(void *scanner, const char *fmt, ...);
+/** Error reporting function for the selection parser. */
+void
+_gmx_selparser_error(void *scanner, const char *fmt, ...);
+
+/** Allocates and initializes a constant \c t_selexpr_value. */
+t_selexpr_value *
+_gmx_selexpr_create_value(e_selvalue_t type);
+/** Allocates and initializes an expression \c t_selexpr_value. */
+t_selexpr_value *
+_gmx_selexpr_create_value_expr(struct t_selelem *expr);
+/** Allocates and initializes a \c t_selexpr_param. */
+t_selexpr_param *
+_gmx_selexpr_create_param(char *name);
+
+/** Frees the memory allocated for a chain of values. */
+void
+_gmx_selexpr_free_values(t_selexpr_value *value);
+/** Frees the memory allocated for a chain of parameters. */
+void
+_gmx_selexpr_free_params(t_selexpr_param *param);
+
+/** Propagates the flags for selection elements. */
+int
+_gmx_selelem_update_flags(struct t_selelem *sel, void *scanner);
+
+/** Initializes the method parameter data of \ref SEL_EXPRESSION and
+ * \ref SEL_MODIFIER elements. */
+void
+_gmx_selelem_init_method_params(struct t_selelem *sel, void *scanner);
+/** Initializes the method for a \ref SEL_EXPRESSION selection element. */
+void
+_gmx_selelem_set_method(struct t_selelem *sel,
+ struct gmx_ana_selmethod_t *method, void *scanner);
+
+/** Creates a \c t_selelem for arithmetic expression evaluation. */
+struct t_selelem *
+_gmx_sel_init_arithmetic(struct t_selelem *left, struct t_selelem *right,
+ char op, void *scanner);
+/** Creates a \c t_selelem for comparsion expression evaluation. */
+struct t_selelem *
+_gmx_sel_init_comparison(struct t_selelem *left, struct t_selelem *right,
+ char *cmpop, void *scanner);
+/** Creates a \c t_selelem for a keyword expression from the parsed data. */
+struct t_selelem *
+_gmx_sel_init_keyword(struct gmx_ana_selmethod_t *method,
+ t_selexpr_value *args, const char *rpost, void *scanner);
+/** Creates a \c t_selelem for a method expression from the parsed data. */
+struct t_selelem *
+_gmx_sel_init_method(struct gmx_ana_selmethod_t *method,
+ t_selexpr_param *params, const char *rpost,
+ void *scanner);
+/** Creates a \c t_selelem for a modifier expression from the parsed data. */
+struct t_selelem *
+_gmx_sel_init_modifier(struct gmx_ana_selmethod_t *mod, t_selexpr_param *params,
+ struct t_selelem *sel, void *scanner);
+/** Creates a \c t_selelem for evaluation of reference positions. */
+struct t_selelem *
+_gmx_sel_init_position(struct t_selelem *expr, const char *type, void *scanner);
+
+/** Creates a \c t_selelem for a constant position. */
+struct t_selelem *
+_gmx_sel_init_const_position(real x, real y, real z);
+/** Creates a \c t_selelem for a index group expression using group name. */
+struct t_selelem *
+_gmx_sel_init_group_by_name(const char *name, void *scanner);
+/** Creates a \c t_selelem for a index group expression using group index. */
+struct t_selelem *
+_gmx_sel_init_group_by_id(int id, void *scanner);
+/** Creates a \c t_selelem for a variable reference */
+struct t_selelem *
+_gmx_sel_init_variable_ref(struct t_selelem *sel);
+
+/** Creates a root \c t_selelem for a selection. */
+struct t_selelem *
+_gmx_sel_init_selection(char *name, struct t_selelem *sel, void *scanner);
+/** Creates a root \c t_selelem elements for a variable assignment. */
+struct t_selelem *
+_gmx_sel_assign_variable(char *name, struct t_selelem *expr, void *scanner);
+/** Appends a root \c t_selelem to a selection collection. */
+struct t_selelem *
+_gmx_sel_append_selection(struct t_selelem *sel, struct t_selelem *last,
+ void *scanner);
+/** Check whether the parser should finish. */
+gmx_bool
+_gmx_sel_parser_should_finish(void *scanner);
+
+/** Handle empty commands. */
+void
+_gmx_sel_handle_empty_cmd(void *scanner);
+/** Process help commands. */
+void
+_gmx_sel_handle_help_cmd(char *topic, void *scanner);
+
+/* In params.c */
+/** Initializes an array of parameters based on input from the selection parser. */
+gmx_bool
+_gmx_sel_parse_params(t_selexpr_param *pparams, int nparam,
+ struct gmx_ana_selparam_t *param, struct t_selelem *root,
+ void *scanner);
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal
+ * \page poscalcengine Position calculation engine
+ *
+ * The header file \ref poscalc.h defines an API for calculating positions
+ * in an automated way. This is useful mostly in the selection engine, in
+ * particular with dynamic selections, because the same COM/COG positions
+ * may be needed in several contexts. The API makes it possible to
+ * optimize the evaluation such that any heavy calculation is only done once,
+ * and the results just copied if needed more than once.
+ * The functions also provide a convenient interface for keeping the whole
+ * \c gmx_ana_pos_t structure up-to-date.
+ *
+ * A new collection of position calculations is allocated with
+ * gmx_ana_poscalc_coll_create().
+ * Calculations within one collection should share the same topology, and
+ * they are optimized. Calculations in different collections do not interact.
+ * The topology for a collection can be set with
+ * gmx_ana_poscalc_coll_set_topology().
+ * This needs to be done before calling gmx_ana_poscalc_set_maxindex() for
+ * any calculation in the collection, unless that calculation does not
+ * require topology information.
+ * All memory allocated for a collection and the calculations in it can be
+ * freed with gmx_ana_poscalc_coll_free().
+ *
+ * A new calculation is created with gmx_ana_poscalc_create().
+ * If flags need to be adjusted later, gmx_ana_poscalc_set_flags() can be
+ * used.
+ * After the flags are final, the largest possible index group for which the
+ * positions are needed has to be set with gmx_ana_poscalc_set_maxindex().
+ * gmx_ana_poscalc_coll_set_topology() should have been called before this
+ * function is called.
+ * After the above calls, gmx_ana_poscalc_init_pos() can be used to initialize
+ * output to a \c gmx_ana_pos_t structure. Several different structures can be
+ * initialized for the same calculation; the only requirement is that the
+ * structure passed later to gmx_ana_poscalc_update() has been initialized
+ * properly.
+ * The memory allocated for a calculation can be freed with
+ * gmx_ana_poscalc_free().
+ *
+ * The position evaluation is simple: gmx_ana_poscalc_init_frame() should be
+ * called once for each frame, and gmx_ana_poscalc_update() can then be called
+ * for each calculation that is needed for that frame.
+ *
+ * It is also possible to initialize the calculations based on a type provided
+ * as a string.
+ * The possible strings are returned by gmx_ana_poscalc_create_type_enum(),
+ * and the string can be converted to the parameters for
+ * gmx_ana_poscalc_create() using gmx_ana_poscalc_type_from_enum().
+ * gmx_ana_poscalc_create_enum() is also provided for convenience.
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in poscalc.h.
+ *
+ * \todo
+ * There is probably some room for optimization in the calculation of
+ * positions with bases.
+ * In particular, the current implementation may do a lot of unnecessary
+ * copying.
+ * The interface would need to be changed to make it possible to use the
+ * same output positions for several calculations.
+ *
+ * \todo
+ * The current algorithm for setting up base calculations could be improved
+ * in cases when there are calculations that cannot use a common base but
+ * still overlap partially (e.g., with three calculations A, B, and C
+ * such that A could use both B and C as a base, but B and C cannot use the
+ * same base).
+ * Setting up the bases in an optimal manner in every possible situation can
+ * be quite difficult unless several bases are allowed for one calculation,
+ * but better heuristics could probably be implemented.
+ * For best results, the setup should probably be postponed (at least
+ * partially) to gmx_ana_poscalc_init_eval().
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <macros.h>
+#include <smalloc.h>
+#include <typedefs.h>
+#include <pbc.h>
+#include <vec.h>
+
+#include "gromacs/selection/centerofmass.h"
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/position.h"
+
+/*! \internal \brief
+ * Collection of \c gmx_ana_poscalc_t structures for the same topology.
+ *
+ * Calculations within the same structure are optimized to eliminate duplicate
+ * calculations.
+ */
+struct gmx_ana_poscalc_coll_t
+{
+ /*! \brief
+ * Topology data.
+ *
+ * Can be NULL if none of the calculations require topology data or if
+ * gmx_ana_poscalc_coll_set_topology() has not been called.
+ */
+ t_topology *top;
+ /** Pointer to the first data structure. */
+ gmx_ana_poscalc_t *first;
+ /** Pointer to the last data structure. */
+ gmx_ana_poscalc_t *last;
+ /** Whether the collection has been initialized for evaluation. */
+ gmx_bool bInit;
+};
+
+/*! \internal \brief
+ * Data structure for position calculation.
+ */
+struct gmx_ana_poscalc_t
+{
+ /*! \brief
+ * Type of calculation.
+ *
+ * This field may differ from the type requested by the user, because
+ * it is changed internally to the most effective calculation.
+ * For example, if the user requests a COM calculation for residues
+ * consisting of single atoms, it is simply set to POS_ATOM.
+ * To provide a consistent interface to the user, the field \p itype
+ * should be used when information should be given out.
+ */
+ e_poscalc_t type;
+ /*! \brief
+ * Flags for calculation options.
+ *
+ * See \ref poscalc_flags "documentation of the flags".
+ */
+ int flags;
+
+ /*! \brief
+ * Type for the created indices.
+ *
+ * This field always agrees with the type that the user requested, but
+ * may differ from \p type.
+ */
+ e_index_t itype;
+ /*! \brief
+ * Block data for the calculation.
+ */
+ t_blocka b;
+ /*! \brief
+ * Mapping from the blocks to the blocks of \p sbase.
+ *
+ * If \p sbase is NULL, this field is also.
+ */
+ int *baseid;
+ /*! \brief
+ * Maximum evaluation group.
+ */
+ gmx_ana_index_t gmax;
+
+ /** Position storage for calculations that are used as a base. */
+ gmx_ana_pos_t *p;
+
+ /** TRUE if the positions have been evaluated for the current frame. */
+ gmx_bool bEval;
+ /*! \brief
+ * Base position data for this calculation.
+ *
+ * If not NULL, the centers required by this calculation have
+ * already been calculated in \p sbase.
+ * The structure pointed by \p sbase is always a static calculation.
+ */
+ struct gmx_ana_poscalc_t *sbase;
+ /** Next structure in the linked list of calculations. */
+ struct gmx_ana_poscalc_t *next;
+ /** Previous structure in the linked list of calculations. */
+ struct gmx_ana_poscalc_t *prev;
+ /** Number of references to this structure. */
+ int refcount;
+ /** Collection this calculation belongs to. */
+ gmx_ana_poscalc_coll_t *coll;
+};
+
+//! Strings returned by gmx_ana_poscalc_create_type_enum().
+static const char *const poscalc_enum_strings[] = {
+ "atom",
+ "res_com", "res_cog",
+ "mol_com", "mol_cog",
+ "whole_res_com", "whole_res_cog",
+ "whole_mol_com", "whole_mol_cog",
+ "part_res_com", "part_res_cog",
+ "part_mol_com", "part_mol_cog",
+ "dyn_res_com", "dyn_res_cog",
+ "dyn_mol_com", "dyn_mol_cog",
+ NULL,
+};
+//! Number of elements in ::poscalc_enum_strings.
+#define NENUM asize(poscalc_enum_strings)
+
+/*! \brief
+ * Returns the partition type for a given position type.
+ *
+ * \param [in] type \c e_poscalc_t value to convert.
+ * \returns Corresponding \c e_indet_t.
+ */
+static e_index_t
+index_type_for_poscalc(e_poscalc_t type)
+{
+ switch(type)
+ {
+ case POS_ATOM: return INDEX_ATOM;
+ case POS_RES: return INDEX_RES;
+ case POS_MOL: return INDEX_MOL;
+ case POS_ALL: return INDEX_ALL;
+ case POS_ALL_PBC: return INDEX_ALL;
+ }
+ return INDEX_UNKNOWN;
+}
+
+/*!
+ * \param[in] post String (typically an enum command-line argument).
+ * Allowed values: 'atom', 'res_com', 'res_cog', 'mol_com', 'mol_cog',
+ * or one of the last four prepended by 'whole_', 'part_', or 'dyn_'.
+ * \param[out] type \c e_poscalc_t corresponding to \p post.
+ * \param[in,out] flags Flags corresponding to \p post.
+ * On input, the flags should contain the default flags.
+ * On exit, the flags \ref POS_MASS, \ref POS_COMPLMAX and
+ * \ref POS_COMPLWHOLE have been set according to \p post
+ * (the completion flags are left at the default values if no completion
+ * prefix is given).
+ * \returns 0 if \p post is one of the valid strings, EINVAL otherwise.
+ *
+ * \attention
+ * Checking is not complete, and other values than those listed above
+ * may be accepted for \p post, but the results are undefined.
+ */
+int
+gmx_ana_poscalc_type_from_enum(const char *post, e_poscalc_t *type, int *flags)
+{
+ const char *ptr;
+
+ if (post[0] == 'a')
+ {
+ *type = POS_ATOM;
+ *flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
+ return 0;
+ }
+
+ /* Process the prefix */
+ ptr = post;
+ if (post[0] == 'w')
+ {
+ *flags &= ~POS_COMPLMAX;
+ *flags |= POS_COMPLWHOLE;
+ ptr = post + 6;
+ }
+ else if (post[0] == 'p')
+ {
+ *flags &= ~POS_COMPLWHOLE;
+ *flags |= POS_COMPLMAX;
+ ptr = post + 5;
+ }
+ else if (post[0] == 'd')
+ {
+ *flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
+ ptr = post + 4;
+ }
+
+ if (ptr[0] == 'r')
+ {
+ *type = POS_RES;
+ }
+ else if (ptr[0] == 'm')
+ {
+ *type = POS_MOL;
+ }
+ else
+ {
+ gmx_incons("unknown position calculation type");
+ return EINVAL;
+ }
+ if (ptr[6] == 'm')
+ {
+ *flags |= POS_MASS;
+ }
+ else if (ptr[6] == 'g')
+ {
+ *flags &= ~POS_MASS;
+ }
+ else
+ {
+ gmx_incons("unknown position calculation type");
+ return EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] bAtom If TRUE, the "atom" value is included.
+ * \returns NULL-terminated array of strings that contains the string
+ * values acceptable for gmx_ana_poscalc_type_from_enum().
+ *
+ * The first string in the returned list is always NULL to allow the list to
+ * be used with Gromacs command-line parsing.
+ */
+const char **
+gmx_ana_poscalc_create_type_enum(gmx_bool bAtom)
+{
+ const char **pcenum;
+ size_t i;
+
+ if (bAtom)
+ {
+ snew(pcenum, NENUM+1);
+ for (i = 0; i < NENUM; ++i)
+ {
+ pcenum[i+1] = poscalc_enum_strings[i];
+ }
+ }
+ else
+ {
+ snew(pcenum, NENUM+1-1);
+ for (i = 1; i < NENUM; ++i)
+ {
+ pcenum[i] = poscalc_enum_strings[i];
+ }
+ }
+ pcenum[0] = NULL;
+ return pcenum;
+}
+
+/*!
+ * \param[out] pccp Allocated position calculation collection.
+ * \returns 0 for success.
+ */
+int
+gmx_ana_poscalc_coll_create(gmx_ana_poscalc_coll_t **pccp)
+{
+ gmx_ana_poscalc_coll_t *pcc;
+
+ snew(pcc, 1);
+ pcc->top = NULL;
+ pcc->first = NULL;
+ pcc->last = NULL;
+ pcc->bInit = FALSE;
+ *pccp = pcc;
+ return 0;
+}
+
+/*!
+ * \param[in,out] pcc Position calculation collection data structure.
+ * \param[in] top Topology data structure.
+ *
+ * This function should be called to set the topology before using
+ * gmx_ana_poscalc_set_maxindex() for any calculation that requires
+ * topology information.
+ */
+void
+gmx_ana_poscalc_coll_set_topology(gmx_ana_poscalc_coll_t *pcc, t_topology *top)
+{
+ pcc->top = top;
+}
+
+/*!
+ * \param[in] pcc Position calculation collection to free.
+ *
+ * The pointer \p pcc is invalid after the call.
+ * Any calculations in the collection are also freed, no matter how many
+ * references to them are left.
+ */
+void
+gmx_ana_poscalc_coll_free(gmx_ana_poscalc_coll_t *pcc)
+{
+ while (pcc->first)
+ {
+ gmx_ana_poscalc_free(pcc->first);
+ }
+ sfree(pcc);
+}
+
+/*!
+ * \param[in] fp File handle to receive the output.
+ * \param[in] pcc Position calculation collection to print.
+ *
+ * The output is very technical, making this function mainly useful for
+ * debugging purposes.
+ */
+void
+gmx_ana_poscalc_coll_print_tree(FILE *fp, gmx_ana_poscalc_coll_t *pcc)
+{
+ gmx_ana_poscalc_t *pc;
+ int i, j;
+
+ fprintf(fp, "Position calculations:\n");
+ i = 1;
+ pc = pcc->first;
+ while (pc)
+ {
+ fprintf(fp, "%2d ", i);
+ switch (pc->type)
+ {
+ case POS_ATOM: fprintf(fp, "ATOM"); break;
+ case POS_RES: fprintf(fp, "RES"); break;
+ case POS_MOL: fprintf(fp, "MOL"); break;
+ case POS_ALL: fprintf(fp, "ALL"); break;
+ case POS_ALL_PBC: fprintf(fp, "ALL_PBC"); break;
+ }
+ if (pc->itype != index_type_for_poscalc(pc->type))
+ {
+ fprintf(fp, " (");
+ switch (pc->itype)
+ {
+ case INDEX_UNKNOWN: fprintf(fp, "???"); break;
+ case INDEX_ATOM: fprintf(fp, "ATOM"); break;
+ case INDEX_RES: fprintf(fp, "RES"); break;
+ case INDEX_MOL: fprintf(fp, "MOL"); break;
+ case INDEX_ALL: fprintf(fp, "ALL"); break;
+ }
+ fprintf(fp, ")");
+ }
+ fprintf(fp, " flg=");
+ if (pc->flags & POS_MASS)
+ {
+ fprintf(fp, "M");
+ }
+ if (pc->flags & POS_DYNAMIC)
+ {
+ fprintf(fp, "D");
+ }
+ if (pc->flags & POS_MASKONLY)
+ {
+ fprintf(fp, "A");
+ }
+ if (pc->flags & POS_COMPLMAX)
+ {
+ fprintf(fp, "Cm");
+ }
+ if (pc->flags & POS_COMPLWHOLE)
+ {
+ fprintf(fp, "Cw");
+ }
+ if (!pc->flags)
+ {
+ fprintf(fp, "0");
+ }
+ fprintf(fp, " nr=%d nra=%d", pc->b.nr, pc->b.nra);
+ fprintf(fp, " refc=%d", pc->refcount);
+ fprintf(fp, "\n");
+ if (pc->gmax.nalloc_index > 0)
+ {
+ fprintf(fp, " Group: ");
+ if (pc->gmax.isize > 20)
+ {
+ fprintf(fp, " %d atoms", pc->gmax.isize);
+ }
+ else
+ {
+ for (j = 0; j < pc->gmax.isize; ++j)
+ {
+ fprintf(fp, " %d", pc->gmax.index[j] + 1);
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ if (pc->b.nalloc_a > 0)
+ {
+ fprintf(fp, " Atoms: ");
+ if (pc->b.nra > 20)
+ {
+ fprintf(fp, " %d atoms", pc->b.nra);
+ }
+ else
+ {
+ for (j = 0; j < pc->b.nra; ++j)
+ {
+ fprintf(fp, " %d", pc->b.a[j] + 1);
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ if (pc->b.nalloc_index > 0)
+ {
+ fprintf(fp, " Blocks:");
+ if (pc->b.nr > 20)
+ {
+ fprintf(fp, " %d pcs", pc->b.nr);
+ }
+ else
+ {
+ for (j = 0; j <= pc->b.nr; ++j)
+ {
+ fprintf(fp, " %d", pc->b.index[j]);
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ if (pc->sbase)
+ {
+ gmx_ana_poscalc_t *base;
+
+ fprintf(fp, " Base: ");
+ j = 1;
+ base = pcc->first;
+ while (base && base != pc->sbase)
+ {
+ ++j;
+ base = base->next;
+ }
+ fprintf(fp, "%d", j);
+ if (pc->baseid && pc->b.nr <= 20)
+ {
+ fprintf(fp, " id:");
+ for (j = 0; j < pc->b.nr; ++j)
+ {
+ fprintf(fp, " %d", pc->baseid[j]+1);
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ ++i;
+ pc = pc->next;
+ }
+}
+
+/*! \brief
+ * Inserts a position calculation structure into its collection.
+ *
+ * \param pc Data structure to insert.
+ * \param before Data structure before which to insert
+ * (NULL = insert at end).
+ *
+ * Inserts \p pc to its collection before \p before.
+ * If \p before is NULL, \p pc is appended to the list.
+ */
+static void
+insert_poscalc(gmx_ana_poscalc_t *pc, gmx_ana_poscalc_t *before)
+{
+ if (before == NULL)
+ {
+ pc->next = NULL;
+ pc->prev = pc->coll->last;
+ if (pc->coll->last)
+ {
+ pc->coll->last->next = pc;
+ }
+ pc->coll->last = pc;
+ }
+ else
+ {
+ pc->prev = before->prev;
+ pc->next = before;
+ if (before->prev)
+ {
+ before->prev->next = pc;
+ }
+ before->prev = pc;
+ }
+ if (!pc->prev)
+ {
+ pc->coll->first = pc;
+ }
+}
+
+/*! \brief
+ * Removes a position calculation structure from its collection.
+ *
+ * \param pc Data structure to remove.
+ *
+ * Removes \p pc from its collection.
+ */
+static void
+remove_poscalc(gmx_ana_poscalc_t *pc)
+{
+ if (pc->prev)
+ {
+ pc->prev->next = pc->next;
+ }
+ else if (pc == pc->coll->first)
+ {
+ pc->coll->first = pc->next;
+ }
+ if (pc->next)
+ {
+ pc->next->prev = pc->prev;
+ }
+ else if (pc == pc->coll->last)
+ {
+ pc->coll->last = pc->prev;
+ }
+ pc->prev = pc->next = NULL;
+}
+
+/*! \brief
+ * Initializes position calculation using the maximum possible input index.
+ *
+ * \param[in,out] pc Position calculation data structure.
+ * \param[in] g Maximum index group for the calculation.
+ * \param[in] bBase Whether \p pc will be used as a base or not.
+ *
+ * \p bBase affects on how the \p pc->gmax field is initialized.
+ */
+static void
+set_poscalc_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g, gmx_bool bBase)
+{
+ gmx_ana_index_make_block(&pc->b, pc->coll->top, g, pc->itype, pc->flags & POS_COMPLWHOLE);
+ /* Set the type to POS_ATOM if the calculation in fact is such. */
+ if (pc->b.nr == pc->b.nra)
+ {
+ pc->type = POS_ATOM;
+ pc->flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
+ }
+ /* Set the POS_COMPLWHOLE flag if the calculation in fact always uses
+ * complete residues and molecules. */
+ if (!(pc->flags & POS_COMPLWHOLE)
+ && (!(pc->flags & POS_DYNAMIC) || (pc->flags & POS_COMPLMAX))
+ && (pc->type == POS_RES || pc->type == POS_MOL)
+ && gmx_ana_index_has_complete_elems(g, pc->itype, pc->coll->top))
+ {
+ pc->flags &= ~POS_COMPLMAX;
+ pc->flags |= POS_COMPLWHOLE;
+ }
+ /* Setup the gmax field */
+ if ((pc->flags & POS_COMPLWHOLE) && !bBase && pc->b.nra > g->isize)
+ {
+ gmx_ana_index_copy(&pc->gmax, g, TRUE);
+ sfree(pc->gmax.name);
+ pc->gmax.name = NULL;
+ }
+ else
+ {
+ gmx_ana_index_set(&pc->gmax, pc->b.nra, pc->b.a, NULL, 0);
+ }
+}
+
+/*! \brief
+ * Checks whether a position calculation should use a base at all.
+ *
+ * \param[in] pc Position calculation data to check.
+ * \returns TRUE if \p pc can use a base and gets some benefit out of it,
+ * FALSE otherwise.
+ */
+static gmx_bool
+can_use_base(gmx_ana_poscalc_t *pc)
+{
+ /* For atoms, it should be faster to do a simple copy, so don't use a
+ * base. */
+ if (pc->type == POS_ATOM)
+ {
+ return FALSE;
+ }
+ /* For dynamic selections that do not use completion, it is not possible
+ * to use a base. */
+ if ((pc->type == POS_RES || pc->type == POS_MOL)
+ && (pc->flags & POS_DYNAMIC) && !(pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
+ {
+ return FALSE;
+ }
+ /* Dynamic calculations for a single position cannot use a base. */
+ if ((pc->type == POS_ALL || pc->type == POS_ALL_PBC)
+ && (pc->flags & POS_DYNAMIC))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*! \brief
+ * Checks whether two position calculations should use a common base.
+ *
+ * \param[in] pc1 Calculation 1 to check for.
+ * \param[in] pc2 Calculation 2 to check for.
+ * \param[in] g1 Index group structure that contains the atoms from
+ * \p pc1.
+ * \param[in,out] g Working space, should have enough allocated memory to
+ * contain the intersection of the atoms in \p pc1 and \p pc2.
+ * \returns TRUE if the two calculations should be merged to use a common
+ * base, FALSE otherwise.
+ */
+static gmx_bool
+should_merge(gmx_ana_poscalc_t *pc1, gmx_ana_poscalc_t *pc2,
+ gmx_ana_index_t *g1, gmx_ana_index_t *g)
+{
+ gmx_ana_index_t g2;
+
+ /* Do not merge calculations with different mass weighting. */
+ if ((pc1->flags & POS_MASS) != (pc2->flags & POS_MASS))
+ {
+ return FALSE;
+ }
+ /* Avoid messing up complete calculations. */
+ if ((pc1->flags & POS_COMPLWHOLE) != (pc2->flags & POS_COMPLWHOLE))
+ {
+ return FALSE;
+ }
+ /* Find the overlap between the calculations. */
+ gmx_ana_index_set(&g2, pc2->b.nra, pc2->b.a, NULL, 0);
+ gmx_ana_index_intersection(g, g1, &g2);
+ /* Do not merge if there is no overlap. */
+ if (g->isize == 0)
+ {
+ return FALSE;
+ }
+ /* Full completion calculations always match if the type is correct. */
+ if ((pc1->flags & POS_COMPLWHOLE) && (pc2->flags & POS_COMPLWHOLE)
+ && pc1->type == pc2->type)
+ {
+ return TRUE;
+ }
+ /* The calculations also match if the intersection consists of full
+ * blocks. */
+ if (gmx_ana_index_has_full_ablocks(g, &pc1->b)
+ && gmx_ana_index_has_full_ablocks(g, &pc2->b))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*! \brief
+ * Creates a static base for position calculation.
+ *
+ * \param pc Data structure to copy.
+ * \returns Pointer to a newly allocated base for \p pc.
+ *
+ * Creates and returns a deep copy of \p pc, but clears the
+ * \ref POS_DYNAMIC and \ref POS_MASKONLY flags.
+ * The newly created structure is set as the base (\c gmx_ana_poscalc_t::sbase)
+ * of \p pc and inserted in the collection before \p pc.
+ */
+static gmx_ana_poscalc_t *
+create_simple_base(gmx_ana_poscalc_t *pc)
+{
+ gmx_ana_poscalc_t *base;
+ int flags;
+ int rc;
+
+ flags = pc->flags & ~(POS_DYNAMIC | POS_MASKONLY);
+ rc = gmx_ana_poscalc_create(&base, pc->coll, pc->type, flags);
+ if (rc != 0)
+ {
+ gmx_fatal(FARGS, "position calculation base creation failed");
+ }
+ set_poscalc_maxindex(base, &pc->gmax, TRUE);
+
+ snew(base->p, 1);
+
+ pc->sbase = base;
+ remove_poscalc(base);
+ insert_poscalc(base, pc);
+
+ return base;
+}
+
+/*! \brief
+ * Merges a calculation into another calculation such that the new calculation
+ * can be used as a base.
+ *
+ * \param[in,out] base Base calculation to merge to.
+ * \param[in,out] pc Position calculation to merge to \p base.
+ *
+ * After the call, \p base can be used as a base for \p pc (or any calculation
+ * that used it as a base).
+ * It is assumed that any overlap between \p base and \p pc is in complete
+ * blocks, i.e., that the merge is possible.
+ */
+static void
+merge_to_base(gmx_ana_poscalc_t *base, gmx_ana_poscalc_t *pc)
+{
+ gmx_ana_index_t gp, gb, g;
+ int isize, bnr;
+ int i, j, bi, bj, bo;
+
+ base->flags |= pc->flags & (POS_VELOCITIES | POS_FORCES);
+ gmx_ana_index_set(&gp, pc->b.nra, pc->b.a, NULL, 0);
+ gmx_ana_index_set(&gb, base->b.nra, base->b.a, NULL, 0);
+ isize = gmx_ana_index_difference_size(&gp, &gb);
+ if (isize > 0)
+ {
+ gmx_ana_index_clear(&g);
+ gmx_ana_index_reserve(&g, base->b.nra + isize);
+ /* Find the new blocks */
+ gmx_ana_index_difference(&g, &gp, &gb);
+ /* Count the blocks in g */
+ i = bi = bnr = 0;
+ while (i < g.isize)
+ {
+ while (pc->b.a[pc->b.index[bi]] != g.index[i])
+ {
+ ++bi;
+ }
+ i += pc->b.index[bi+1] - pc->b.index[bi];
+ ++bnr;
+ ++bi;
+ }
+ /* Merge the atoms into a temporary structure */
+ gmx_ana_index_merge(&g, &gb, &g);
+ /* Merge the blocks */
+ srenew(base->b.index, base->b.nr + bnr + 1);
+ i = g.isize - 1;
+ bi = base->b.nr - 1;
+ bj = pc->b.nr - 1;
+ bo = base->b.nr + bnr - 1;
+ base->b.index[bo+1] = i + 1;
+ while (bo >= 0)
+ {
+ if (bi < 0 || base->b.a[base->b.index[bi+1]-1] != g.index[i])
+ {
+ i -= pc->b.index[bj+1] - pc->b.index[bj];
+ --bj;
+ }
+ else
+ {
+ if (bj >= 0 && pc->b.a[pc->b.index[bj+1]-1] == g.index[i])
+ {
+ --bj;
+ }
+ i -= base->b.index[bi+1] - base->b.index[bi];
+ --bi;
+ }
+ base->b.index[bo] = i + 1;
+ --bo;
+ }
+ base->b.nr += bnr;
+ base->b.nalloc_index += bnr;
+ sfree(base->b.a);
+ base->b.nra = g.isize;
+ base->b.a = g.index;
+ base->b.nalloc_a = g.isize;
+ /* Refresh the gmax field */
+ gmx_ana_index_set(&base->gmax, base->b.nra, base->b.a, NULL, 0);
+ }
+}
+
+/*! \brief
+ * Merges two bases into one.
+ *
+ * \param[in,out] tbase Base calculation to merge to.
+ * \param[in] mbase Base calculation to merge to \p tbase.
+ *
+ * After the call, \p mbase has been freed and \p tbase is used as the base
+ * for all calculations that previously had \p mbase as their base.
+ * It is assumed that any overlap between \p tbase and \p mbase is in complete
+ * blocks, i.e., that the merge is possible.
+ */
+static void
+merge_bases(gmx_ana_poscalc_t *tbase, gmx_ana_poscalc_t *mbase)
+{
+ gmx_ana_poscalc_t *pc;
+
+ merge_to_base(tbase, mbase);
+ remove_poscalc(mbase);
+ /* Set tbase as the base for all calculations that had mbase */
+ pc = tbase->coll->first;
+ while (pc)
+ {
+ if (pc->sbase == mbase)
+ {
+ pc->sbase = tbase;
+ tbase->refcount++;
+ }
+ pc = pc->next;
+ }
+ /* Free mbase */
+ mbase->refcount = 0;
+ gmx_ana_poscalc_free(mbase);
+}
+
+/*! \brief
+ * Setups the static base calculation for a position calculation.
+ *
+ * \param[in,out] pc Position calculation to setup the base for.
+ */
+static void
+setup_base(gmx_ana_poscalc_t *pc)
+{
+ gmx_ana_poscalc_t *base, *pbase, *next;
+ gmx_ana_index_t gp, g;
+
+ /* Exit immediately if pc should not have a base. */
+ if (!can_use_base(pc))
+ {
+ return;
+ }
+
+ gmx_ana_index_set(&gp, pc->b.nra, pc->b.a, NULL, 0);
+ gmx_ana_index_clear(&g);
+ gmx_ana_index_reserve(&g, pc->b.nra);
+ pbase = pc;
+ base = pc->coll->first;
+ while (base)
+ {
+ /* Save the next calculation so that we can safely delete base */
+ next = base->next;
+ /* Skip pc, calculations that already have a base (we should match the
+ * base instead), as well as calculations that should not have a base.
+ * If the above conditions are met, check whether we should do a
+ * merge.
+ */
+ if (base != pc && !base->sbase && can_use_base(base)
+ && should_merge(pbase, base, &gp, &g))
+ {
+ /* Check whether this is the first base found */
+ if (pbase == pc)
+ {
+ /* Create a real base if one is not present */
+ if (!base->p)
+ {
+ pbase = create_simple_base(base);
+ }
+ else
+ {
+ pbase = base;
+ }
+ /* Make it a base for pc as well */
+ merge_to_base(pbase, pc);
+ pc->sbase = pbase;
+ pbase->refcount++;
+ }
+ else /* This was not the first base */
+ {
+ if (!base->p)
+ {
+ /* If it is not a real base, just make the new base as
+ * the base for it as well. */
+ merge_to_base(pbase, base);
+ base->sbase = pbase;
+ pbase->refcount++;
+ }
+ else
+ {
+ /* If base is a real base, merge it with the new base
+ * and delete it. */
+ merge_bases(pbase, base);
+ }
+ }
+ gmx_ana_index_set(&gp, pbase->b.nra, pbase->b.a, NULL, 0);
+ gmx_ana_index_reserve(&g, pc->b.nra);
+ }
+ /* Proceed to the next unchecked calculation */
+ base = next;
+ }
+
+ gmx_ana_index_deinit(&g);
+
+ /* If no base was found, create one if one is required */
+ if (!pc->sbase && (pc->flags & POS_DYNAMIC)
+ && (pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
+ {
+ create_simple_base(pc);
+ }
+}
+
+/*!
+ * \param[out] pcp Position calculation data structure pointer to initialize.
+ * \param[in,out] pcc Position calculation collection.
+ * \param[in] type Type of calculation.
+ * \param[in] flags Flags for setting calculation options
+ * (see \ref poscalc_flags "documentation of the flags").
+ * \returns 0 on success.
+ */
+int
+gmx_ana_poscalc_create(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
+ e_poscalc_t type, int flags)
+{
+ gmx_ana_poscalc_t *pc;
+
+ snew(pc, 1);
+ pc->type = type;
+ pc->itype = index_type_for_poscalc(type);
+ gmx_ana_poscalc_set_flags(pc, flags);
+ pc->refcount = 1;
+ pc->coll = pcc;
+ insert_poscalc(pc, NULL);
+ *pcp = pc;
+ return 0;
+}
+
+/*!
+ * \param[out] pcp Position calculation data structure pointer to initialize.
+ * \param[in,out] pcc Position calculation collection.
+ * \param[in] post One of the strings acceptable for
+ * gmx_ana_poscalc_type_from_enum().
+ * \param[in] flags Flags for setting calculation options
+ * (see \ref poscalc_flags "documentation of the flags").
+ * \returns 0 on success, a non-zero error value on error.
+ *
+ * This is a convenience wrapper for gmx_ana_poscalc_create().
+ * \p flags sets the default calculation options if not overridden by \p post;
+ * see gmx_ana_poscalc_type_from_enum().
+ *
+ * \see gmx_ana_poscalc_create(), gmx_ana_poscalc_type_from_enum()
+ */
+int
+gmx_ana_poscalc_create_enum(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
+ const char *post, int flags)
+{
+ e_poscalc_t type;
+ int cflags;
+ int rc;
+
+ cflags = flags;
+ rc = gmx_ana_poscalc_type_from_enum(post, &type, &cflags);
+ if (rc != 0)
+ {
+ *pcp = NULL;
+ return rc;
+ }
+ return gmx_ana_poscalc_create(pcp, pcc, type, cflags);
+}
+
+/*!
+ * \param[in,out] pc Position calculation data structure.
+ * \param[in] flags New flags.
+ *
+ * \p flags are added to the old flags.
+ * If calculation type is \ref POS_ATOM, \ref POS_MASS is automatically
+ * cleared.
+ * If both \ref POS_DYNAMIC and \ref POS_MASKONLY are provided,
+ * \ref POS_DYNAMIC is cleared.
+ * If calculation type is not \ref POS_RES or \ref POS_MOL,
+ * \ref POS_COMPLMAX and \ref POS_COMPLWHOLE are automatically cleared.
+ */
+void
+gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t *pc, int flags)
+{
+ if (pc->type == POS_ATOM)
+ {
+ flags &= ~POS_MASS;
+ }
+ if (flags & POS_MASKONLY)
+ {
+ flags &= ~POS_DYNAMIC;
+ }
+ if (pc->type != POS_RES && pc->type != POS_MOL)
+ {
+ flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
+ }
+ pc->flags |= flags;
+}
+
+/*!
+ * \param[in,out] pc Position calculation data structure.
+ * \param[in] g Maximum index group for the calculation.
+ *
+ * Subsequent calls to gmx_ana_poscalc_update() should use only subsets of
+ * \p g for evaluation.
+ *
+ * The topology should have been set for the collection of which \p pc is
+ * a member.
+ */
+void
+gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g)
+{
+ set_poscalc_maxindex(pc, g, FALSE);
+ setup_base(pc);
+}
+
+/*!
+ * \param[in] pc Position calculation data structure.
+ * \param[out] p Output positions.
+ *
+ * Calls to gmx_ana_poscalc_update() using \p pc should use only positions
+ * initialized with this function.
+ * The \c p->g pointer is initialized to point to an internal group that
+ * contains the maximum index group set with gmx_ana_poscalc_set_maxindex().
+ */
+void
+gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t *pc, gmx_ana_pos_t *p)
+{
+ gmx_ana_indexmap_init(&p->m, &pc->gmax, pc->coll->top, pc->itype);
+ if (!(pc->flags & POS_DYNAMIC))
+ {
+ gmx_ana_indexmap_set_static(&p->m, &pc->b);
+ }
+ gmx_ana_pos_reserve(p, p->m.nr, 0);
+ if (pc->flags & POS_VELOCITIES)
+ {
+ gmx_ana_pos_reserve_velocities(p);
+ }
+ if (pc->flags & POS_FORCES)
+ {
+ gmx_ana_pos_reserve_forces(p);
+ }
+ gmx_ana_pos_set_nr(p, p->m.nr);
+ gmx_ana_pos_set_evalgrp(p, &pc->gmax);
+}
+
+/*!
+ * \param pc Position calculation data to be freed.
+ *
+ * The \p pc pointer is invalid after the call.
+ */
+void
+gmx_ana_poscalc_free(gmx_ana_poscalc_t *pc)
+{
+ if (!pc)
+ {
+ return;
+ }
+
+ pc->refcount--;
+ if (pc->refcount > 0)
+ {
+ return;
+ }
+
+ remove_poscalc(pc);
+ if (pc->b.nalloc_index > 0)
+ {
+ sfree(pc->b.index);
+ }
+ if (pc->b.nalloc_a > 0)
+ {
+ sfree(pc->b.a);
+ }
+ if (pc->flags & POS_COMPLWHOLE)
+ {
+ gmx_ana_index_deinit(&pc->gmax);
+ }
+ if (pc->p)
+ {
+ gmx_ana_pos_free(pc->p);
+ }
+ if (pc->sbase)
+ {
+ gmx_ana_poscalc_free(pc->sbase);
+ sfree(pc->baseid);
+ }
+ sfree(pc);
+}
+
+/*!
+ * \param[in] pc Position calculation data to query.
+ * \returns TRUE if \p pc requires topology for initialization and/or
+ * evaluation, FALSE otherwise.
+ */
+gmx_bool
+gmx_ana_poscalc_requires_top(gmx_ana_poscalc_t *pc)
+{
+ if ((pc->flags & POS_MASS) || pc->type == POS_RES || pc->type == POS_MOL)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*!
+ * \param[in,out] pcc Position calculation collection to initialize.
+ *
+ * This function does some final initialization of the data structures in the
+ * collection to prepare them for evaluation.
+ * After this function has been called, it is no longer possible to add new
+ * calculations to the collection.
+ *
+ * This function is automatically called by gmx_ana_poscalc_init_frame()
+ * if not called by the user earlier.
+ * Multiple calls to the function are ignored.
+ */
+void
+gmx_ana_poscalc_init_eval(gmx_ana_poscalc_coll_t *pcc)
+{
+ gmx_ana_poscalc_t *pc;
+ int bi, bj;
+
+ if (pcc->bInit)
+ {
+ return;
+ }
+ pc = pcc->first;
+ while (pc)
+ {
+ /* Initialize position storage for base calculations */
+ if (pc->p)
+ {
+ gmx_ana_poscalc_init_pos(pc, pc->p);
+ }
+ /* Construct the mapping of the base positions */
+ if (pc->sbase)
+ {
+ snew(pc->baseid, pc->b.nr);
+ for (bi = bj = 0; bi < pc->b.nr; ++bi, ++bj)
+ {
+ while (pc->sbase->b.a[pc->sbase->b.index[bj]] != pc->b.a[pc->b.index[bi]])
+ {
+ ++bj;
+ }
+ pc->baseid[bi] = bj;
+ }
+ }
+ /* Free the block data for dynamic calculations */
+ if (pc->flags & POS_DYNAMIC)
+ {
+ if (pc->b.nalloc_index > 0)
+ {
+ sfree(pc->b.index);
+ pc->b.nalloc_index = 0;
+ }
+ if (pc->b.nalloc_a > 0)
+ {
+ sfree(pc->b.a);
+ pc->b.nalloc_a = 0;
+ }
+ }
+ pc = pc->next;
+ }
+ pcc->bInit = TRUE;
+}
+
+/*!
+ * \param[in,out] pcc Position calculation collection to initialize.
+ *
+ * Clears the evaluation flag for all calculations.
+ * Should be called for each frame before calling gmx_ana_poscalc_update().
+ *
+ * This function is automatically called by gmx_ana_do() for each
+ * frame, and should not be called by the user unless gmx_ana_do() is
+ * not being used.
+ *
+ * This function calls gmx_ana_poscalc_init_eval() automatically if it has
+ * not been called earlier.
+ */
+void
+gmx_ana_poscalc_init_frame(gmx_ana_poscalc_coll_t *pcc)
+{
+ gmx_ana_poscalc_t *pc;
+
+ if (!pcc->bInit)
+ {
+ gmx_ana_poscalc_init_eval(pcc);
+ }
+ /* Clear the evaluation flags */
+ pc = pcc->first;
+ while (pc)
+ {
+ pc->bEval = FALSE;
+ pc = pc->next;
+ }
+}
+
+/*!
+ * \param[in] pc Position calculation data.
+ * \param[in,out] p Output positions, initialized previously with
+ * gmx_ana_poscalc_init_pos() using \p pc.
+ * \param[in] g Index group to use for the update.
+ * \param[in] fr Current frame.
+ * \param[in] pbc PBC data, or NULL if no PBC should be used.
+ *
+ * gmx_ana_poscalc_init_frame() should be called for each frame before calling
+ * this function.
+ */
+void
+gmx_ana_poscalc_update(gmx_ana_poscalc_t *pc, gmx_ana_pos_t *p,
+ gmx_ana_index_t *g, t_trxframe *fr, t_pbc *pbc)
+{
+ int i, j, bi, bj;
+
+ if (pc->bEval == TRUE && !(pc->flags & POS_MASKONLY))
+ {
+ return;
+ }
+ if (pc->sbase)
+ {
+ gmx_ana_poscalc_update(pc->sbase, NULL, NULL, fr, pbc);
+ }
+ if (!p)
+ {
+ p = pc->p;
+ }
+ if (!g)
+ {
+ g = &pc->gmax;
+ }
+ gmx_ana_pos_set_evalgrp(p, g);
+
+ /* Update the index map */
+ if (pc->flags & POS_DYNAMIC)
+ {
+ gmx_ana_indexmap_update(&p->m, g, FALSE);
+ p->nr = p->m.nr;
+ }
+ else if (pc->flags & POS_MASKONLY)
+ {
+ gmx_ana_indexmap_update(&p->m, g, TRUE);
+ if (pc->bEval)
+ return;
+ }
+ if (!(pc->flags & POS_DYNAMIC))
+ {
+ pc->bEval = TRUE;
+ }
+
+ /* Evaluate the positions */
+ if (pc->sbase)
+ {
+ /* TODO: It might be faster to evaluate the positions within this
+ * loop instead of in the beginning. */
+ if (pc->flags & POS_DYNAMIC)
+ {
+ for (bi = 0; bi < p->nr; ++bi)
+ {
+ bj = pc->baseid[p->m.refid[bi]];
+ copy_rvec(pc->sbase->p->x[bj], p->x[bi]);
+ }
+ if (p->v)
+ {
+ for (bi = 0; bi < p->nr; ++bi)
+ {
+ bj = pc->baseid[p->m.refid[bi]];
+ copy_rvec(pc->sbase->p->v[bj], p->v[bi]);
+ }
+ }
+ if (p->f)
+ {
+ for (bi = 0; bi < p->nr; ++bi)
+ {
+ bj = pc->baseid[p->m.refid[bi]];
+ copy_rvec(pc->sbase->p->f[bj], p->f[bi]);
+ }
+ }
+ }
+ else
+ {
+ for (bi = 0; bi < p->nr; ++bi)
+ {
+ bj = pc->baseid[bi];
+ copy_rvec(pc->sbase->p->x[bj], p->x[bi]);
+ }
+ if (p->v)
+ {
+ for (bi = 0; bi < p->nr; ++bi)
+ {
+ bj = pc->baseid[bi];
+ copy_rvec(pc->sbase->p->v[bj], p->v[bi]);
+ }
+ }
+ if (p->f)
+ {
+ for (bi = 0; bi < p->nr; ++bi)
+ {
+ bj = pc->baseid[bi];
+ copy_rvec(pc->sbase->p->f[bj], p->f[bi]);
+ }
+ }
+ }
+ }
+ else /* pc->sbase is NULL */
+ {
+ if (pc->flags & POS_DYNAMIC)
+ {
+ pc->b.nr = p->m.mapb.nr;
+ pc->b.index = p->m.mapb.index;
+ pc->b.nra = g->isize;
+ pc->b.a = g->index;
+ }
+ if (p->v && !fr->bV)
+ {
+ for (i = 0; i < pc->b.nra; ++i)
+ {
+ clear_rvec(p->v[i]);
+ }
+ }
+ if (p->f && !fr->bF)
+ {
+ for (i = 0; i < pc->b.nra; ++i)
+ {
+ clear_rvec(p->f[i]);
+ }
+ }
+ /* Here, we assume that the topology has been properly initialized,
+ * and do not check the return values of gmx_calc_comg*(). */
+ switch (pc->type)
+ {
+ case POS_ATOM:
+ for (i = 0; i < pc->b.nra; ++i)
+ {
+ copy_rvec(fr->x[pc->b.a[i]], p->x[i]);
+ }
+ if (p->v && fr->bV)
+ {
+ for (i = 0; i < pc->b.nra; ++i)
+ {
+ copy_rvec(fr->v[pc->b.a[i]], p->v[i]);
+ }
+ }
+ if (p->f && fr->bF)
+ {
+ for (i = 0; i < pc->b.nra; ++i)
+ {
+ copy_rvec(fr->f[pc->b.a[i]], p->f[i]);
+ }
+ }
+ break;
+ case POS_ALL:
+ gmx_calc_comg(pc->coll->top, fr->x, pc->b.nra, pc->b.a,
+ pc->flags & POS_MASS, p->x[0]);
+ if (p->v && fr->bV)
+ {
+ gmx_calc_comg(pc->coll->top, fr->v, pc->b.nra, pc->b.a,
+ pc->flags & POS_MASS, p->v[0]);
+ }
+ if (p->f && fr->bF)
+ {
+ gmx_calc_comg_f(pc->coll->top, fr->f, pc->b.nra, pc->b.a,
+ pc->flags & POS_MASS, p->f[0]);
+ }
+ break;
+ case POS_ALL_PBC:
+ gmx_calc_comg_pbc(pc->coll->top, fr->x, pbc, pc->b.nra, pc->b.a,
+ pc->flags & POS_MASS, p->x[0]);
+ if (p->v && fr->bV)
+ {
+ gmx_calc_comg(pc->coll->top, fr->v, pc->b.nra, pc->b.a,
+ pc->flags & POS_MASS, p->v[0]);
+ }
+ if (p->f && fr->bF)
+ {
+ gmx_calc_comg_f(pc->coll->top, fr->f, pc->b.nra, pc->b.a,
+ pc->flags & POS_MASS, p->f[0]);
+ }
+ break;
+ default:
+ gmx_calc_comg_blocka(pc->coll->top, fr->x, &pc->b,
+ pc->flags & POS_MASS, p->x);
+ if (p->v && fr->bV)
+ {
+ gmx_calc_comg_blocka(pc->coll->top, fr->v, &pc->b,
+ pc->flags & POS_MASS, p->v);
+ }
+ if (p->f && fr->bF)
+ {
+ gmx_calc_comg_blocka(pc->coll->top, fr->f, &pc->b,
+ pc->flags & POS_MASS, p->f);
+ }
+ break;
+ }
+ }
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief API for structured and optimized calculation of positions.
+ *
+ * The functions in this header are used internally by the analysis library
+ * to calculate positions.
+ * They can also be used in user code, but in most cases there should be no
+ * need. Instead, one should write an analysis tool such that it gets all
+ * positions through selections.
+ *
+ * The API is documented in more detail on a separate page:
+ * \ref poscalcengine.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_POSCALC_H
+#define GMX_SELECTION_POSCALC_H
+
+#include "../legacyheaders/typedefs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \name Flags for position calculation.
+ * \anchor poscalc_flags
+ */
+/*@{*/
+/*! \brief
+ * Use mass weighting.
+ *
+ * If this flag is set, the positions will be calculated using mass weighting,
+ * i.e., one gets center-of-mass positions.
+ * Without the flag, center-of-geometry positions are calculated.
+ * Does not have any effect if the calculation type is \ref POS_ATOM.
+ */
+#define POS_MASS 1
+/*! \brief
+ * Calculate positions for the same atoms in residues/molecules.
+ *
+ * If this flag is set, the positions are always calculated using the same
+ * atoms for each residue/molecule, even if the evaluation group contains only
+ * some of the atoms for some frames.
+ * The group passed to gmx_ana_poscalc_set_maxindex() is used to determine
+ * the atoms to use for the calculation.
+ *
+ * Has no effect unless \ref POS_DYNAMIC is set or if the calculation type
+ * is not \ref POS_RES of \ref POS_MOL.
+ */
+#define POS_COMPLMAX 2
+/*! \brief
+ * Calculate positions for whole residues/molecules.
+ *
+ * If this flag is set, the positions will be calculated for whole
+ * residues/molecules, even if the group contains only some of the atoms in
+ * the residue/molecule.
+ *
+ * Has no effect unless the calculation type is \ref POS_RES or \ref POS_MOL.
+ */
+#define POS_COMPLWHOLE 4
+/*! \brief
+ * Enable handling of changing calculation groups.
+ *
+ * Can be used for static calculations as well, but implies a small
+ * performance penalty.
+ */
+#define POS_DYNAMIC 16
+/*! \brief
+ * Update \c gmx_ana_pos_t::m dynamically for an otherwise static
+ * calculation.
+ *
+ * Has effect only if \ref POS_DYNAMIC is not set.
+ */
+#define POS_MASKONLY 32
+/*! \brief
+ * Calculate velocities of the positions.
+ */
+#define POS_VELOCITIES 64
+/*! \brief
+ * Calculate forces on the positions.
+ */
+#define POS_FORCES 128
+/*@}*/
+
+/** Specifies the type of positions to be calculated. */
+typedef enum
+{
+ POS_ATOM, /**< Copy atomic coordinates. */
+ POS_RES, /**< Calculate center for each residue. */
+ POS_MOL, /**< Calculate center for each molecule. */
+ POS_ALL, /**< Calculate center for the whole group. */
+ POS_ALL_PBC /**< Calculate center for the whole group with PBC. */
+} e_poscalc_t;
+
+/** Collection of \c gmx_ana_poscalc_t structures for the same topology. */
+typedef struct gmx_ana_poscalc_coll_t gmx_ana_poscalc_coll_t;
+/** Data structure for position calculation. */
+typedef struct gmx_ana_poscalc_t gmx_ana_poscalc_t;
+
+struct gmx_ana_index_t;
+struct gmx_ana_pos_t;
+
+/** Converts a string to parameters for gmx_ana_poscalc_create(). */
+int
+gmx_ana_poscalc_type_from_enum(const char *post, e_poscalc_t *type, int *flags);
+/** Creates a list of strings for position enum parameter handling. */
+const char **
+gmx_ana_poscalc_create_type_enum(gmx_bool bAtom);
+
+/** Creates a new position calculation collection object. */
+int
+gmx_ana_poscalc_coll_create(gmx_ana_poscalc_coll_t **pccp);
+/** Sets the topology for a position calculation collection. */
+void
+gmx_ana_poscalc_coll_set_topology(gmx_ana_poscalc_coll_t *pcc, t_topology *top);
+/** Frees memory allocated for a position calculation collection. */
+void
+gmx_ana_poscalc_coll_free(gmx_ana_poscalc_coll_t *pcc);
+/** Prints information about calculations in a position calculation collection. */
+void
+gmx_ana_poscalc_coll_print_tree(FILE *fp, gmx_ana_poscalc_coll_t *pcc);
+
+/** Creates a new position calculation. */
+int
+gmx_ana_poscalc_create(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
+ e_poscalc_t type, int flags);
+/** Creates a new position calculation based on an enum value. */
+int
+gmx_ana_poscalc_create_enum(gmx_ana_poscalc_t **pcp, gmx_ana_poscalc_coll_t *pcc,
+ const char *post, int flags);
+/** Sets the flags for position calculation. */
+void
+gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t *pc, int flags);
+/** Sets the maximum possible input index group for position calculation. */
+void
+gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t *pc, struct gmx_ana_index_t *g);
+/** Initializes positions for position calculation output. */
+void
+gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t *pc, struct gmx_ana_pos_t *p);
+/** Frees the memory allocated for position calculation. */
+void
+gmx_ana_poscalc_free(gmx_ana_poscalc_t *pc);
+/** Returns TRUE if the position calculation requires topology information. */
+gmx_bool
+gmx_ana_poscalc_requires_top(gmx_ana_poscalc_t *pc);
+
+/** Initializes evaluation for a position calculation collection. */
+void
+gmx_ana_poscalc_init_eval(gmx_ana_poscalc_coll_t *pcc);
+/** Initializes a position calculation collection for a new frame. */
+void
+gmx_ana_poscalc_init_frame(gmx_ana_poscalc_coll_t *pcc);
+/** Updates a single COM/COG structure for a frame. */
+void
+gmx_ana_poscalc_update(gmx_ana_poscalc_t *pc,
+ struct gmx_ana_pos_t *p, struct gmx_ana_index_t *g,
+ t_trxframe *fr, t_pbc *pbc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in position.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include <smalloc.h>
+#include <typedefs.h>
+#include <vec.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/position.h"
+
+/*!
+ * \param[out] pos Output structure.
+ *
+ * Any contents of \p pos are discarded without freeing.
+ */
+void
+gmx_ana_pos_clear(gmx_ana_pos_t *pos)
+{
+ pos->nr = 0;
+ pos->x = NULL;
+ pos->v = NULL;
+ pos->f = NULL;
+ gmx_ana_indexmap_clear(&pos->m);
+ pos->g = NULL;
+ pos->nalloc_x = 0;
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ * \param[in] n Maximum number of positions.
+ * \param[in] isize Maximum number of atoms.
+ *
+ * Ensures that enough memory is allocated in \p pos to calculate \p n
+ * positions from \p isize atoms.
+ */
+void
+gmx_ana_pos_reserve(gmx_ana_pos_t *pos, int n, int isize)
+{
+ if (pos->nalloc_x < n)
+ {
+ pos->nalloc_x = n;
+ srenew(pos->x, n);
+ if (pos->v)
+ {
+ srenew(pos->v, n);
+ }
+ if (pos->f)
+ {
+ srenew(pos->f, n);
+ }
+ }
+ if (isize > 0)
+ {
+ gmx_ana_indexmap_reserve(&pos->m, n, isize);
+ }
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * Currently, this function can only be called after gmx_ana_pos_reserve()
+ * has been called at least once with a \p n > 0.
+ */
+void
+gmx_ana_pos_reserve_velocities(gmx_ana_pos_t *pos)
+{
+ assert(pos->nalloc_x > 0);
+ if (!pos->v)
+ {
+ snew(pos->v, pos->nalloc_x);
+ }
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * Currently, this function can only be called after gmx_ana_pos_reserve()
+ * has been called at least once with a \p n > 0.
+ */
+void
+gmx_ana_pos_reserve_forces(gmx_ana_pos_t *pos)
+{
+ assert(pos->nalloc_x > 0);
+ if (!pos->f)
+ {
+ snew(pos->f, pos->nalloc_x);
+ }
+}
+
+/*!
+ * \param[out] pos Position data structure to initialize.
+ * \param[in] x Position vector to use.
+ */
+void
+gmx_ana_pos_init_const(gmx_ana_pos_t *pos, rvec x)
+{
+ gmx_ana_pos_clear(pos);
+ pos->nr = 1;
+ snew(pos->x, 1);
+ snew(pos->v, 1);
+ snew(pos->f, 1);
+ pos->nalloc_x = 1;
+ copy_rvec(x, pos->x[0]);
+ clear_rvec(pos->v[0]);
+ clear_rvec(pos->f[0]);
+ gmx_ana_indexmap_init(&pos->m, NULL, NULL, INDEX_UNKNOWN);
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * Frees any memory allocated within \p pos.
+ * The pointer \p pos itself is not freed.
+ *
+ * \see gmx_ana_pos_free()
+ */
+void
+gmx_ana_pos_deinit(gmx_ana_pos_t *pos)
+{
+ pos->nr = 0;
+ sfree(pos->x); pos->x = NULL;
+ sfree(pos->v); pos->v = NULL;
+ sfree(pos->f); pos->f = NULL;
+ pos->nalloc_x = 0;
+ gmx_ana_indexmap_deinit(&pos->m);
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * Frees any memory allocated for \p pos.
+ * The pointer \p pos is also freed, and is invalid after the call.
+ *
+ * \see gmx_ana_pos_deinit()
+ */
+void
+gmx_ana_pos_free(gmx_ana_pos_t *pos)
+{
+ gmx_ana_pos_deinit(pos);
+ sfree(pos);
+}
+
+/*!
+ * \param[in,out] dest Destination positions.
+ * \param[in] src Source positions.
+ * \param[in] bFirst If TRUE, memory is allocated for \p dest and a full
+ * copy is made; otherwise, only variable parts are copied, and no memory
+ * is allocated.
+ *
+ * \p dest should have been initialized somehow (calloc() is enough).
+ */
+void
+gmx_ana_pos_copy(gmx_ana_pos_t *dest, gmx_ana_pos_t *src, gmx_bool bFirst)
+{
+ if (bFirst)
+ {
+ gmx_ana_pos_reserve(dest, src->nr, 0);
+ if (src->v)
+ {
+ gmx_ana_pos_reserve_velocities(dest);
+ }
+ if (src->f)
+ {
+ gmx_ana_pos_reserve_forces(dest);
+ }
+ }
+ dest->nr = src->nr;
+ memcpy(dest->x, src->x, dest->nr*sizeof(*dest->x));
+ if (dest->v)
+ {
+ memcpy(dest->v, src->v, dest->nr*sizeof(*dest->v));
+ }
+ if (dest->f)
+ {
+ memcpy(dest->f, src->f, dest->nr*sizeof(*dest->f));
+ }
+ gmx_ana_indexmap_copy(&dest->m, &src->m, bFirst);
+ dest->g = src->g;
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ * \param[in] nr Number of positions.
+ */
+void
+gmx_ana_pos_set_nr(gmx_ana_pos_t *pos, int nr)
+{
+ pos->nr = nr;
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ * \param g Evaluation group.
+ *
+ * The old group, if any, is discarded.
+ * Note that only a pointer to \p g is stored; it is the responsibility of
+ * the caller to ensure that \p g is not freed while it can be accessed
+ * through \p pos.
+ */
+void
+gmx_ana_pos_set_evalgrp(gmx_ana_pos_t *pos, gmx_ana_index_t *g)
+{
+ pos->g = g;
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * Sets the number of positions to 0.
+ */
+void
+gmx_ana_pos_empty_init(gmx_ana_pos_t *pos)
+{
+ pos->nr = 0;
+ pos->m.nr = 0;
+ pos->m.mapb.nr = 0;
+ pos->m.b.nr = 0;
+ pos->m.b.nra = 0;
+ /* This should not really be necessary, but do it for safety... */
+ pos->m.mapb.index[0] = 0;
+ pos->m.b.index[0] = 0;
+ /* This function should only be used to construct all the possible
+ * positions, so the result should always be static. */
+ pos->m.bStatic = TRUE;
+ pos->m.bMapStatic = TRUE;
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * Sets the number of positions to 0.
+ */
+void
+gmx_ana_pos_empty(gmx_ana_pos_t *pos)
+{
+ pos->nr = 0;
+ pos->m.nr = 0;
+ pos->m.mapb.nr = 0;
+ /* This should not really be necessary, but do it for safety... */
+ pos->m.mapb.index[0] = 0;
+ /* We set the flags to TRUE, although really in the empty state they
+ * should be FALSE. This makes it possible to update the flags in
+ * gmx_ana_pos_append(), and just make a simple check in
+ * gmx_ana_pos_append_finish(). */
+ pos->m.bStatic = TRUE;
+ pos->m.bMapStatic = TRUE;
+}
+
+/*!
+ * \param[in,out] dest Data structure to which the new position is appended.
+ * \param[in,out] g Data structure to which the new atoms are appended.
+ * \param[in] src Data structure from which the position is copied.
+ * \param[in] i Index in \p from to copy.
+ */
+void
+gmx_ana_pos_append_init(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
+ gmx_ana_pos_t *src, int i)
+{
+ int j, k;
+
+ j = dest->nr;
+ copy_rvec(src->x[i], dest->x[j]);
+ if (dest->v)
+ {
+ if (src->v)
+ {
+ copy_rvec(src->v[i], dest->v[j]);
+ }
+ else
+ {
+ clear_rvec(dest->v[j]);
+ }
+ }
+ if (dest->f)
+ {
+ if (src->f)
+ {
+ copy_rvec(src->f[i], dest->f[j]);
+ }
+ else
+ {
+ clear_rvec(dest->f[j]);
+ }
+ }
+ dest->m.refid[j] = j;
+ dest->m.mapid[j] = src->m.mapid[i];
+ dest->m.orgid[j] = src->m.orgid[i];
+ for (k = src->m.mapb.index[i]; k < src->m.mapb.index[i+1]; ++k)
+ {
+ g->index[g->isize++] = src->g->index[k];
+ dest->m.b.a[dest->m.b.nra++] = src->m.b.a[k];
+ }
+ dest->m.mapb.index[j+1] = g->isize;
+ dest->m.b.index[j+1] = g->isize;
+ dest->nr++;
+ dest->m.nr = dest->nr;
+ dest->m.mapb.nr = dest->nr;
+ dest->m.b.nr = dest->nr;
+}
+
+/*!
+ * \param[in,out] dest Data structure to which the new position is appended
+ * (can be NULL, in which case only \p g is updated).
+ * \param[in,out] g Data structure to which the new atoms are appended.
+ * \param[in] src Data structure from which the position is copied.
+ * \param[in] i Index in \p src to copy.
+ * \param[in] refid Reference ID in \p out
+ * (all negative values are treated as -1).
+ *
+ * If \p dest is NULL, the value of \p refid is not used.
+ */
+void
+gmx_ana_pos_append(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
+ gmx_ana_pos_t *src, int i, int refid)
+{
+ int j, k;
+
+ for (k = src->m.mapb.index[i]; k < src->m.mapb.index[i+1]; ++k)
+ {
+ g->index[g->isize++] = src->g->index[k];
+ }
+ if (dest)
+ {
+ j = dest->nr;
+ if (dest->v)
+ {
+ if (src->v)
+ {
+ copy_rvec(src->v[i], dest->v[j]);
+ }
+ else
+ {
+ clear_rvec(dest->v[j]);
+ }
+ }
+ if (dest->f)
+ {
+ if (src->f)
+ {
+ copy_rvec(src->f[i], dest->f[j]);
+ }
+ else
+ {
+ clear_rvec(dest->f[j]);
+ }
+ }
+ copy_rvec(src->x[i], dest->x[j]);
+ if (refid < 0)
+ {
+ dest->m.refid[j] = -1;
+ dest->m.bStatic = FALSE;
+ /* If we are using masks, there is no need to alter the
+ * mapid field. */
+ }
+ else
+ {
+ if (refid != j)
+ {
+ dest->m.bStatic = FALSE;
+ dest->m.bMapStatic = FALSE;
+ }
+ dest->m.refid[j] = refid;
+ /* Use the original IDs from the output structure to correctly
+ * handle user customization. */
+ dest->m.mapid[j] = dest->m.orgid[refid];
+ }
+ dest->m.mapb.index[j+1] = g->isize;
+ dest->nr++;
+ dest->m.nr = dest->nr;
+ dest->m.mapb.nr = dest->nr;
+ }
+}
+
+/*!
+ * \param[in,out] pos Position data structure.
+ *
+ * After gmx_ana_pos_empty(), internal state of the position data structure
+ * is not consistent before this function is called. This function should be
+ * called after any gmx_ana_pos_append() calls have been made.
+ */
+void
+gmx_ana_pos_append_finish(gmx_ana_pos_t *pos)
+{
+ if (pos->m.nr != pos->m.b.nr)
+ {
+ pos->m.bStatic = FALSE;
+ pos->m.bMapStatic = FALSE;
+ }
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief API for handling positions.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_POSITION_H
+#define GMX_SELECTION_POSITION_H
+
+#include "../legacyheaders/types/simple.h"
+
+#include "indexutil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \brief
+ * Stores a set of positions together with their origins.
+ */
+typedef struct gmx_ana_pos_t
+{
+ /*! \brief
+ * Number of positions.
+ */
+ int nr;
+ /*! \brief
+ * Array of positions.
+ */
+ rvec *x;
+ /*! \brief
+ * Velocities (can be NULL).
+ */
+ rvec *v;
+ /*! \brief
+ * Forces (can be NULL).
+ */
+ rvec *f;
+ /*! \brief
+ * Mapping of the current positions to the original group.
+ *
+ * \see gmx_ana_indexmap_t
+ */
+ gmx_ana_indexmap_t m;
+ /*! \brief
+ * Pointer to the current evaluation group.
+ */
+ gmx_ana_index_t *g;
+ /*! \brief
+ * Number of elements allocated for \c x.
+ */
+ int nalloc_x;
+} gmx_ana_pos_t;
+
+/** Initializes an empty position structure. */
+void
+gmx_ana_pos_clear(gmx_ana_pos_t *pos);
+/** Ensures that enough memory has been allocated to store positions. */
+void
+gmx_ana_pos_reserve(gmx_ana_pos_t *pos, int n, int isize);
+/** Request memory allocation for velocities. */
+void
+gmx_ana_pos_reserve_velocities(gmx_ana_pos_t *pos);
+/** Request memory allocation for forces. */
+void
+gmx_ana_pos_reserve_forces(gmx_ana_pos_t *pos);
+/** Initializes a \c gmx_ana_pos_t to represent a constant position. */
+void
+gmx_ana_pos_init_const(gmx_ana_pos_t *pos, rvec x);
+/** Frees the memory allocated for position storage. */
+void
+gmx_ana_pos_deinit(gmx_ana_pos_t *pos);
+/** Frees the memory allocated for positions. */
+void
+gmx_ana_pos_free(gmx_ana_pos_t *pos);
+/** Copies the evaluated positions to a preallocated data structure. */
+void
+gmx_ana_pos_copy(gmx_ana_pos_t *dest, gmx_ana_pos_t *src, gmx_bool bFirst);
+
+/** Sets the number of positions in a position structure. */
+void
+gmx_ana_pos_set_nr(gmx_ana_pos_t *pos, int n);
+/** Sets the evaluation group of a position data structure. */
+void
+gmx_ana_pos_set_evalgrp(gmx_ana_pos_t *pos, gmx_ana_index_t *g);
+/** Empties a position data structure with full initialization. */
+void
+gmx_ana_pos_empty_init(gmx_ana_pos_t *pos);
+/** Empties a position data structure. */
+void
+gmx_ana_pos_empty(gmx_ana_pos_t *pos);
+/** Appends a position to a preallocated data structure with full
+ * initialization. */
+void
+gmx_ana_pos_append_init(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
+ gmx_ana_pos_t *src, int i);
+/** Appends a position to a preallocated data structure. */
+void
+gmx_ana_pos_append(gmx_ana_pos_t *dest, gmx_ana_index_t *g,
+ gmx_ana_pos_t *src, int i, int refid);
+/** Updates position data structure state after appends. */
+void
+gmx_ana_pos_append_finish(gmx_ana_pos_t *pos);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#!/bin/bash
+#
+# This script runs Bison and/or Flex to regenerate the files as follows:
+# parser.y -> parser.c, parser.h
+# scanner.l -> scanner.c, scanner_flex.h
+# The commands are run only if the generated files are older than the
+# Bison/Flex input files, or if a '-f' flag is provided.
+
+FORCE=
+if [ "x$1" == "x-f" ] ; then
+ FORCE=1
+fi
+
+# For convenience, change to the directory where the files are located
+# if the script is run from the root of the source tree.
+dirname=src/gromacs/selection
+if [[ -f $dirname/parser.y && -f $dirname/scanner.l ]] ; then
+ cd $dirname
+fi
+
+[[ $FORCE || parser.y -nt parser.cpp ]] && bison -t -o parser.cpp --defines=parser.h parser.y
+[[ $FORCE || scanner.l -nt scanner.cpp ]] && flex -o scanner.cpp scanner.l
--- /dev/null
+#line 2 "scanner.cpp"
+
+#line 4 "scanner.cpp"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE _gmx_sel_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via _gmx_sel_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void _gmx_sel_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner );
+
+static void _gmx_sel_yyensure_buffer_stack (yyscan_t yyscanner );
+static void _gmx_sel_yy_load_buffer_state (yyscan_t yyscanner );
+static void _gmx_sel_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER _gmx_sel_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *_gmx_sel_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *_gmx_sel_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void _gmx_sel_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer _gmx_sel_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ _gmx_sel_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ _gmx_sel_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+#define _gmx_sel_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 26
+#define YY_END_OF_BUFFER 27
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[89] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 27, 25, 23, 6, 20, 25, 1, 25, 25, 2,
+ 6, 21, 25, 22, 25, 24, 22, 22, 22, 22,
+ 22, 22, 25, 22, 22, 22, 22, 22, 11, 8,
+ 10, 10, 9, 23, 21, 0, 4, 0, 1, 17,
+ 3, 3, 2, 24, 24, 22, 5, 22, 22, 22,
+ 18, 15, 22, 18, 16, 13, 22, 12, 22, 22,
+ 8, 9, 0, 0, 3, 17, 22, 20, 19, 13,
+ 22, 0, 3, 3, 22, 7, 14, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 1, 1, 7, 1, 1,
+ 1, 1, 8, 1, 8, 9, 1, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 1, 11, 12,
+ 13, 12, 1, 1, 14, 14, 14, 14, 15, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 1, 16, 1, 1, 17, 1, 18, 14, 14, 19,
+
+ 20, 21, 22, 23, 14, 14, 14, 24, 14, 25,
+ 26, 27, 14, 28, 29, 30, 31, 14, 14, 32,
+ 33, 14, 1, 34, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[35] =
+ { 0,
+ 1, 1, 2, 1, 1, 1, 1, 1, 3, 4,
+ 1, 1, 1, 4, 4, 1, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[94] =
+ { 0,
+ 0, 0, 131, 130, 10, 12, 132, 131, 45, 0,
+ 153, 158, 150, 158, 138, 75, 0, 143, 139, 72,
+ 158, 135, 134, 0, 143, 136, 119, 115, 116, 113,
+ 114, 113, 104, 62, 111, 68, 116, 115, 158, 132,
+ 158, 158, 0, 131, 158, 79, 158, 127, 0, 158,
+ 84, 87, 91, 122, 31, 0, 158, 111, 103, 98,
+ 0, 0, 99, 158, 0, 96, 104, 0, 95, 99,
+ 120, 0, 34, 107, 76, 0, 82, 0, 0, 0,
+ 83, 99, 98, 95, 76, 0, 0, 158, 111, 115,
+ 117, 94, 84
+
+ } ;
+
+static yyconst flex_int16_t yy_def[94] =
+ { 0,
+ 88, 1, 1, 1, 1, 1, 1, 1, 88, 9,
+ 88, 88, 88, 88, 88, 89, 90, 88, 88, 91,
+ 88, 88, 88, 92, 88, 91, 92, 92, 92, 92,
+ 92, 92, 88, 92, 92, 92, 92, 92, 88, 88,
+ 88, 88, 93, 88, 88, 89, 88, 88, 90, 88,
+ 88, 88, 91, 91, 91, 92, 88, 92, 92, 92,
+ 92, 92, 92, 88, 92, 92, 92, 92, 92, 92,
+ 88, 93, 88, 88, 91, 92, 92, 92, 92, 92,
+ 92, 88, 88, 88, 92, 92, 92, 0, 88, 88,
+ 88, 88, 88
+
+ } ;
+
+static yyconst flex_int16_t yy_nxt[193] =
+ { 0,
+ 12, 13, 14, 15, 16, 17, 18, 12, 19, 20,
+ 21, 22, 23, 24, 24, 25, 26, 27, 24, 24,
+ 24, 28, 24, 24, 29, 30, 24, 24, 24, 31,
+ 24, 32, 24, 33, 35, 36, 35, 36, 74, 88,
+ 75, 82, 37, 83, 37, 39, 40, 41, 39, 39,
+ 39, 39, 39, 39, 39, 42, 39, 39, 43, 43,
+ 39, 39, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 39, 47,
+ 52, 53, 65, 47, 88, 75, 55, 72, 67, 61,
+ 48, 55, 68, 51, 48, 61, 51, 56, 73, 52,
+
+ 53, 73, 87, 73, 84, 55, 73, 83, 83, 86,
+ 55, 46, 85, 46, 46, 49, 84, 49, 49, 54,
+ 54, 71, 81, 68, 80, 78, 79, 78, 77, 76,
+ 88, 46, 44, 71, 70, 69, 66, 64, 63, 62,
+ 61, 60, 59, 58, 88, 57, 45, 45, 51, 50,
+ 45, 44, 88, 38, 38, 34, 34, 11, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88
+
+ } ;
+
+static yyconst flex_int16_t yy_chk[193] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 5, 5, 6, 6, 55, 55,
+ 55, 73, 5, 73, 6, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 16,
+ 20, 20, 34, 46, 75, 75, 20, 93, 36, 34,
+ 16, 20, 36, 51, 46, 36, 52, 92, 51, 53,
+
+ 53, 52, 85, 51, 84, 53, 52, 83, 82, 81,
+ 53, 89, 77, 89, 89, 90, 74, 90, 90, 91,
+ 91, 71, 70, 69, 67, 66, 63, 60, 59, 58,
+ 54, 48, 44, 40, 38, 37, 35, 33, 32, 31,
+ 30, 29, 28, 27, 26, 25, 23, 22, 19, 18,
+ 15, 13, 11, 8, 7, 4, 3, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88
+
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "scanner.l"
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \cond \internal \file scanner.l
+ * \brief
+ * Tokenizer for the selection language.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ * \endcond
+ */
+/*! \internal \file scanner.cpp
+ * \brief
+ * Generated (from scanner.l by Flex) tokenizer for the selection language.
+ *
+ * \ingroup module_selection
+ */
+#line 46 "scanner.l"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string2.h>
+
+#include "parser.h"
+#include "scanner.h"
+#include "scanner_internal.h"
+
+/* This macro is here to make the actions a bit shorter, since nearly every
+ * action needs this call. */
+#define ADD_TOKEN _gmx_sel_lexer_add_token(yytext, yyleng, state)
+
+#define YY_NO_UNISTD_H 1
+
+
+
+
+#line 576 "scanner.cpp"
+
+#define INITIAL 0
+#define matchof 1
+#define matchbool 2
+#define cmdstart 3
+#define help 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+int _gmx_sel_yylex_init (yyscan_t* scanner);
+
+int _gmx_sel_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int _gmx_sel_yylex_destroy (yyscan_t yyscanner );
+
+int _gmx_sel_yyget_debug (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *_gmx_sel_yyget_in (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *_gmx_sel_yyget_out (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int _gmx_sel_yyget_leng (yyscan_t yyscanner );
+
+char *_gmx_sel_yyget_text (yyscan_t yyscanner );
+
+int _gmx_sel_yyget_lineno (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int _gmx_sel_yywrap (yyscan_t yyscanner );
+#else
+extern int _gmx_sel_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int _gmx_sel_yylex (yyscan_t yyscanner);
+
+#define YY_DECL int _gmx_sel_yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 85 "scanner.l"
+
+
+
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(yyscanner);
+ int retval;
+ /* Return a token if one is pending */
+ retval = _gmx_sel_lexer_process_pending(yylval, state);
+ if (retval != 0)
+ {
+ return retval;
+ }
+ /* Handle the start conditions for 'of' matching */
+ if (state->bMatchOf)
+ {
+ BEGIN(matchof);
+ state->bMatchOf = FALSE;
+ }
+ else if (state->bMatchBool)
+ {
+ BEGIN(matchbool);
+ state->bMatchBool = FALSE;
+ }
+ else if (state->bCmdStart)
+ {
+ BEGIN(cmdstart);
+ }
+ else if (YYSTATE != help)
+ {
+ BEGIN(0);
+ }
+
+
+#line 839 "scanner.cpp"
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ _gmx_sel_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ _gmx_sel_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 89 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 88 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 117 "scanner.l"
+
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 118 "scanner.l"
+{ yylval->i = strtol(yytext, NULL, 10); ADD_TOKEN; return TOK_INT; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 119 "scanner.l"
+{ yylval->r = strtod(yytext, NULL); ADD_TOKEN; return TOK_REAL; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 120 "scanner.l"
+{ yylval->str = gmx_strndup(yytext+1, yyleng-2); ADD_TOKEN; return STR; }
+ YY_BREAK
+case 5:
+/* rule 5 can match eol */
+YY_RULE_SETUP
+#line 122 "scanner.l"
+{ _gmx_sel_lexer_add_token(" ", 1, state); }
+ YY_BREAK
+case 6:
+/* rule 6 can match eol */
+YY_RULE_SETUP
+#line 123 "scanner.l"
+{
+ if (yytext[0] == ';' || state->bInteractive)
+ {
+ rtrim(state->pselstr);
+ return CMD_SEP;
+ }
+ else
+ {
+ _gmx_sel_lexer_add_token(" ", 1, state);
+ }
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 135 "scanner.l"
+{ BEGIN(help); return HELP; }
+ YY_BREAK
+
+case 8:
+YY_RULE_SETUP
+#line 137 "scanner.l"
+
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 138 "scanner.l"
+{ yylval->str = gmx_strndup(yytext, yyleng); return HELP_TOPIC; }
+ YY_BREAK
+case 10:
+/* rule 10 can match eol */
+YY_RULE_SETUP
+#line 139 "scanner.l"
+{ return CMD_SEP; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 140 "scanner.l"
+{ return INVALID; }
+ YY_BREAK
+
+
+case 12:
+YY_RULE_SETUP
+#line 144 "scanner.l"
+{ ADD_TOKEN; yylval->i = 1; return TOK_INT; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 145 "scanner.l"
+{ ADD_TOKEN; yylval->i = 0; return TOK_INT; }
+ YY_BREAK
+
+case 14:
+YY_RULE_SETUP
+#line 147 "scanner.l"
+{ ADD_TOKEN; return GROUP; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 148 "scanner.l"
+{ ADD_TOKEN; return TO; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 149 "scanner.l"
+{ ADD_TOKEN; BEGIN(0); return OF; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 150 "scanner.l"
+{ ADD_TOKEN; return AND; }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 151 "scanner.l"
+{ ADD_TOKEN; return OR; }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 152 "scanner.l"
+{ ADD_TOKEN; return XOR; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 153 "scanner.l"
+{ ADD_TOKEN; return NOT; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 154 "scanner.l"
+{ yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return CMP_OP; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 156 "scanner.l"
+{ return _gmx_sel_lexer_process_identifier(yylval, yytext, yyleng, state); }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 158 "scanner.l"
+{ _gmx_sel_lexer_add_token(" ", 1, state); }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 159 "scanner.l"
+{ yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return STR; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 160 "scanner.l"
+{ ADD_TOKEN; return yytext[0]; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 161 "scanner.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1065 "scanner.cpp"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(matchof):
+case YY_STATE_EOF(matchbool):
+case YY_STATE_EOF(cmdstart):
+case YY_STATE_EOF(help):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * _gmx_sel_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( _gmx_sel_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of _gmx_sel_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ _gmx_sel_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ _gmx_sel_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) _gmx_sel_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 89 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 89 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 88);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ _gmx_sel_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( _gmx_sel_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void _gmx_sel_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ _gmx_sel_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ _gmx_sel_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ _gmx_sel_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ _gmx_sel_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * _gmx_sel_yypop_buffer_state();
+ * _gmx_sel_yypush_buffer_state(new_buffer);
+ */
+ _gmx_sel_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ _gmx_sel_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (_gmx_sel_yywrap()) processing, but the only time this flag
+ * is looked at is after _gmx_sel_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void _gmx_sel_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) _gmx_sel_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) _gmx_sel_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ _gmx_sel_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with _gmx_sel_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ _gmx_sel_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ _gmx_sel_yyfree((void *) b ,yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a _gmx_sel_yyrestart() or at EOF.
+ */
+ static void _gmx_sel_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ _gmx_sel_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then _gmx_sel_yy_init_buffer was _probably_
+ * called from _gmx_sel_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ _gmx_sel_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ _gmx_sel_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from _gmx_sel_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from _gmx_sel_yy_switch_to_buffer. */
+ _gmx_sel_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ _gmx_sel_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ _gmx_sel_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void _gmx_sel_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)_gmx_sel_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)_gmx_sel_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) _gmx_sel_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ _gmx_sel_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to _gmx_sel_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * _gmx_sel_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return _gmx_sel_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to _gmx_sel_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) _gmx_sel_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in _gmx_sel_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = _gmx_sel_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in _gmx_sel_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int _gmx_sel_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int _gmx_sel_yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *_gmx_sel_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *_gmx_sel_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int _gmx_sel_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *_gmx_sel_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void _gmx_sel_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "_gmx_sel_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void _gmx_sel_yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "_gmx_sel_yyset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see _gmx_sel_yy_switch_to_buffer
+ */
+void _gmx_sel_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void _gmx_sel_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int _gmx_sel_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void _gmx_sel_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* _gmx_sel_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int _gmx_sel_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) _gmx_sel_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* _gmx_sel_yylex_init_extra has the same functionality as _gmx_sel_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to _gmx_sel_yyalloc in
+ * the yyextra field.
+ */
+
+int _gmx_sel_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ _gmx_sel_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) _gmx_sel_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ _gmx_sel_yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from _gmx_sel_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * _gmx_sel_yylex_init()
+ */
+ return 0;
+}
+
+/* _gmx_sel_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int _gmx_sel_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ _gmx_sel_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ _gmx_sel_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ _gmx_sel_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ _gmx_sel_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * _gmx_sel_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ _gmx_sel_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *_gmx_sel_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *_gmx_sel_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void _gmx_sel_yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see _gmx_sel_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 161 "scanner.l"
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Parser/scanner interaction functions.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ */
+#ifndef SELECTION_SCANNER_H
+#define SELECTION_SCANNER_H
+
+namespace gmx
+{
+class AbstractErrorReporter;
+}
+
+#include "parser.h"
+
+struct gmx_ana_indexgrps_t;
+struct gmx_ana_selcollection_t;
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+
+/** Initializes the selection scanner. */
+int
+_gmx_sel_init_lexer(yyscan_t *scannerp, struct gmx_ana_selcollection_t *sc,
+ gmx::AbstractErrorReporter *errors,
+ bool bInteractive, int maxnr, bool bGroups,
+ struct gmx_ana_indexgrps_t *grps);
+/** Frees memory allocated for the selection scanner. */
+void
+_gmx_sel_free_lexer(yyscan_t scanner);
+
+/** Returns TRUE if the scanner is interactive. */
+gmx_bool
+_gmx_sel_is_lexer_interactive(yyscan_t scanner);
+/** Returns the selection collection for the scanner. */
+struct gmx_ana_selcollection_t *
+_gmx_sel_lexer_selcollection(yyscan_t scanner);
+/** Returns the error reporter for the scanner. */
+gmx::AbstractErrorReporter *
+_gmx_sel_lexer_error_reporter(yyscan_t scanner);
+/** Returns true if the external index groups for the scanner are set. */
+bool
+_gmx_sel_lexer_has_groups_set(yyscan_t scanner);
+/** Returns the external index groups for the scanner. */
+struct gmx_ana_indexgrps_t *
+_gmx_sel_lexer_indexgrps(yyscan_t scanner);
+/** Returns the number of selections after which the parser should stop. */
+int
+_gmx_sel_lexer_exp_selcount(yyscan_t scanner);
+
+/** Returns a pretty string of the current selection. */
+const char *
+_gmx_sel_lexer_pselstr(yyscan_t scanner);
+/** Clears the current selection string. */
+void
+_gmx_sel_lexer_clear_pselstr(yyscan_t scanner);
+/** Clears the method stack in the scanner in error situations. */
+void
+_gmx_sel_lexer_clear_method_stack(yyscan_t scanner);
+/** Notifies the scanner that a complete method expression has been parsed. */
+void
+_gmx_sel_finish_method(yyscan_t scanner);
+/** Initializes the scanner to scan a file. */
+void
+_gmx_sel_set_lex_input_file(yyscan_t scanner, FILE *fp);
+/** Initializes the scanner to scan a string. */
+void
+_gmx_sel_set_lex_input_str(yyscan_t scanner, const char *str);
+
+/** A wrapper for the actual scanner, used by the Bison parser. */
+int
+_gmx_sel_yyblex(YYSTYPE *yylval, yyscan_t yyscanner);
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \cond \internal \file scanner.l
+ * \brief
+ * Tokenizer for the selection language.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ * \endcond
+ */
+/*! \internal \file scanner.cpp
+ * \brief
+ * Generated (from scanner.l by Flex) tokenizer for the selection language.
+ *
+ * \ingroup module_selection
+ */
+%{
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string2.h>
+
+#include "parser.h"
+#include "scanner.h"
+#include "scanner_internal.h"
+
+/* This macro is here to make the actions a bit shorter, since nearly every
+ * action needs this call. */
+#define ADD_TOKEN _gmx_sel_lexer_add_token(yytext, yyleng, state)
+
+%}
+
+INTEGER [[:digit:]]+
+DSEQ ([[:digit:]]+)
+FRAC (([[:digit:]]*"."{DSEQ})|{DSEQ}".")
+EXP ([eE][+-]?{DSEQ})
+REAL (({FRAC}{EXP}?)|({DSEQ}{EXP}))
+STRING (\"([^\"\\\n]|(\\\"))*\")
+IDENTIFIER ([[:alpha:]][_[:alnum:]]*)
+CMPOP (([<>]=?)|([!=]=))
+COMMENT (#.*)
+
+%option nodefault
+%option noyywrap
+%option reentrant
+%option prefix="_gmx_sel_yy"
+%option header-file="scanner_flex.h"
+%option nounistd
+%option never-interactive
+
+%s matchof
+%s matchbool
+%s cmdstart
+%x help
+
+%%
+
+%{
+ gmx_sel_lexer_t *state = yyget_extra(yyscanner);
+ int retval;
+ /* Return a token if one is pending */
+ retval = _gmx_sel_lexer_process_pending(yylval, state);
+ if (retval != 0)
+ {
+ return retval;
+ }
+ /* Handle the start conditions for 'of' matching */
+ if (state->bMatchOf)
+ {
+ BEGIN(matchof);
+ state->bMatchOf = FALSE;
+ }
+ else if (state->bMatchBool)
+ {
+ BEGIN(matchbool);
+ state->bMatchBool = FALSE;
+ }
+ else if (state->bCmdStart)
+ {
+ BEGIN(cmdstart);
+ }
+ else if (YYSTATE != help)
+ {
+ BEGIN(0);
+ }
+%}
+
+{COMMENT}
+{INTEGER} { yylval->i = strtol(yytext, NULL, 10); ADD_TOKEN; return TOK_INT; }
+{REAL} { yylval->r = strtod(yytext, NULL); ADD_TOKEN; return TOK_REAL; }
+{STRING} { yylval->str = gmx_strndup(yytext+1, yyleng-2); ADD_TOKEN; return STR; }
+
+\\\n { _gmx_sel_lexer_add_token(" ", 1, state); }
+";"|\n {
+ if (yytext[0] == ';' || state->bInteractive)
+ {
+ rtrim(state->pselstr);
+ return CMD_SEP;
+ }
+ else
+ {
+ _gmx_sel_lexer_add_token(" ", 1, state);
+ }
+ }
+
+<cmdstart>help { BEGIN(help); return HELP; }
+<help>{
+[[:blank:]]+
+{IDENTIFIER} { yylval->str = gmx_strndup(yytext, yyleng); return HELP_TOPIC; }
+";"|\n { return CMD_SEP; }
+. { return INVALID; }
+}
+
+<matchbool>{
+yes|on { ADD_TOKEN; yylval->i = 1; return TOK_INT; }
+no|off { ADD_TOKEN; yylval->i = 0; return TOK_INT; }
+}
+group { ADD_TOKEN; return GROUP; }
+to { ADD_TOKEN; return TO; }
+<matchof>of { ADD_TOKEN; BEGIN(0); return OF; }
+and|"&&" { ADD_TOKEN; return AND; }
+or|"||" { ADD_TOKEN; return OR; }
+xor { ADD_TOKEN; return XOR; }
+not|"!" { ADD_TOKEN; return NOT; }
+{CMPOP} { yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return CMP_OP; }
+
+{IDENTIFIER} { return _gmx_sel_lexer_process_identifier(yylval, yytext, yyleng, state); }
+
+[[:blank:]]+ { _gmx_sel_lexer_add_token(" ", 1, state); }
+[_[:alnum:]]+ { yylval->str = gmx_strndup(yytext, yyleng); ADD_TOKEN; return STR; }
+. { ADD_TOKEN; return yytext[0]; }
--- /dev/null
+#ifndef _gmx_sel_yyHEADER_H
+#define _gmx_sel_yyHEADER_H 1
+#define _gmx_sel_yyIN_HEADER 1
+
+#line 6 "scanner_flex.h"
+
+#line 8 "scanner_flex.h"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void _gmx_sel_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner );
+
+YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *_gmx_sel_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *_gmx_sel_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void _gmx_sel_yyfree (void * ,yyscan_t yyscanner );
+
+#define _gmx_sel_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define matchof 1
+#define matchbool 2
+#define cmdstart 3
+#define help 4
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int _gmx_sel_yylex_init (yyscan_t* scanner);
+
+int _gmx_sel_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int _gmx_sel_yylex_destroy (yyscan_t yyscanner );
+
+int _gmx_sel_yyget_debug (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *_gmx_sel_yyget_in (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *_gmx_sel_yyget_out (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int _gmx_sel_yyget_leng (yyscan_t yyscanner );
+
+char *_gmx_sel_yyget_text (yyscan_t yyscanner );
+
+int _gmx_sel_yyget_lineno (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int _gmx_sel_yywrap (yyscan_t yyscanner );
+#else
+extern int _gmx_sel_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int _gmx_sel_yylex (yyscan_t yyscanner);
+
+#define YY_DECL int _gmx_sel_yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 161 "scanner.l"
+
+#line 349 "scanner_flex.h"
+#undef _gmx_sel_yyIN_HEADER
+#endif /* _gmx_sel_yyHEADER_H */
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Helper functions for the selection tokenizer.
+ *
+ * This file implements the functions in the headers scanner.h and
+ * scanner_internal.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+/*! \internal \file scanner_flex.h
+ * \brief Generated (from scanner.l) header file by Flex.
+ *
+ * This file contains definitions of functions that are needed in
+ * scanner_internal.cpp.
+ *
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <typedefs.h>
+#include <smalloc.h>
+#include <string.h>
+
+#include "string2.h"
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+#include "gromacs/selection/selmethod.h"
+
+#include "parsetree.h"
+#include "selectioncollection-impl.h"
+#include "selelem.h"
+#include "symrec.h"
+
+#include "parser.h"
+#include "scanner.h"
+#include "scanner_internal.h"
+
+//! Step in which the allocated memory for pretty-printed input is incremeted.
+#define STRSTORE_ALLOCSTEP 1000
+
+/* These are defined as macros in the generated scanner_flex.h.
+ * We undefine them here to have them as variable names in the subroutines.
+ * There are other ways of doing this, but this is probably the easiest. */
+#undef yylval
+#undef yytext
+#undef yyleng
+
+static gmx_bool
+read_stdin_line(gmx_sel_lexer_t *state)
+{
+ char *ptr = state->inputstr;
+ int max_len = state->nalloc_input;
+ int totlen = 0;
+
+ if (feof(stdin))
+ {
+ return FALSE;
+ }
+ if (state->bInteractive)
+ {
+ fprintf(stderr, "> ");
+ }
+ /* For some reason (at least on my Linux), fgets() doesn't return until
+ * the user presses Ctrl-D _twice_ at the end of a non-empty line.
+ * This can be a bit confusing for users, but there's not much we can
+ * do, and the chances of a normal user noticing this are not very big. */
+ while (fgets(ptr, max_len, stdin) != NULL)
+ {
+ int len = strlen(ptr);
+
+ totlen += len;
+ if (len >= 2 && ptr[len - 1] == '\n' && ptr[len - 2] == '\\')
+ {
+ if (state->bInteractive)
+ {
+ fprintf(stderr, "... ");
+ }
+ }
+ else if ((len >= 1 && ptr[len - 1] == '\n') || len < max_len - 1)
+ {
+ break;
+ }
+ ptr += len;
+ max_len -= len;
+ if (max_len <= 2)
+ {
+ max_len += state->nalloc_input;
+ state->nalloc_input *= 2;
+ len = ptr - state->inputstr;
+ srenew(state->inputstr, state->nalloc_input);
+ ptr = state->inputstr + len;
+ }
+ }
+ if (state->bInteractive && (totlen == 0 || ptr[totlen - 1] != '\n'))
+ {
+ fprintf(stderr, "\n");
+ }
+ if (ferror(stdin))
+ {
+ GMX_ERROR_NORET(gmx::eeInvalidInput, "Selection reading failed");
+ }
+ return totlen > 0;
+}
+
+int
+_gmx_sel_yyblex(YYSTYPE *yylval, yyscan_t yyscanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(yyscanner);
+ gmx_bool bCmdStart;
+ int token;
+
+ if (!state->bBuffer && !state->inputstr)
+ {
+ state->nalloc_input = 1024;
+ snew(state->inputstr, state->nalloc_input);
+ read_stdin_line(state);
+ _gmx_sel_set_lex_input_str(yyscanner, state->inputstr);
+ }
+ bCmdStart = state->bCmdStart;
+ token = _gmx_sel_yylex(yylval, yyscanner);
+ while (state->inputstr && token == 0 && read_stdin_line(state))
+ {
+ _gmx_sel_set_lex_input_str(yyscanner, state->inputstr);
+ token = _gmx_sel_yylex(yylval, yyscanner);
+ }
+ if (token == 0 && !bCmdStart)
+ {
+ token = CMD_SEP;
+ rtrim(state->pselstr);
+ }
+ state->bCmdStart = (token == CMD_SEP);
+ return token;
+}
+
+static int
+init_param_token(YYSTYPE *yylval, gmx_ana_selparam_t *param, gmx_bool bBoolNo)
+{
+ if (bBoolNo)
+ {
+ snew(yylval->str, strlen(param->name) + 3);
+ yylval->str[0] = 'n';
+ yylval->str[1] = 'o';
+ strcpy(yylval->str+2, param->name);
+ }
+ else
+ {
+ yylval->str = param->name ? strdup(param->name) : NULL;
+ }
+ return PARAM;
+}
+
+static int
+init_method_token(YYSTYPE *yylval, gmx_ana_selmethod_t *method, gmx_bool bPosMod,
+ gmx_sel_lexer_t *state)
+{
+ /* If the previous token was not KEYWORD_POS, return EMPTY_POSMOD
+ * before the actual method to work around a limitation in Bison. */
+ if (!bPosMod && method->type != POS_VALUE)
+ {
+ state->nextmethod = method;
+ return EMPTY_POSMOD;
+ }
+ yylval->meth = method;
+ if (!(method->flags & SMETH_MODIFIER) && method->nparams == 0)
+ {
+ /* Keyword */
+ switch (method->type)
+ {
+ case INT_VALUE: return KEYWORD_NUMERIC;
+ case REAL_VALUE: return KEYWORD_NUMERIC;
+ case STR_VALUE: return KEYWORD_STR;
+ case GROUP_VALUE: return KEYWORD_GROUP;
+ default:
+ GMX_ERROR_NORET(gmx::eeInternalError, "Unsupported keyword type");
+ return INVALID;
+ }
+ } else {
+ /* Method with parameters or a modifier */
+ if (method->flags & SMETH_MODIFIER)
+ {
+ /* Remove all methods from the stack */
+ state->msp = -1;
+ if (method->param[1].name == NULL)
+ {
+ state->nextparam = &method->param[1];
+ }
+ }
+ else
+ {
+ if (method->param[0].name == NULL)
+ {
+ state->nextparam = &method->param[0];
+ }
+ }
+ ++state->msp;
+ if (state->msp >= state->mstack_alloc)
+ {
+ state->mstack_alloc += 10;
+ srenew(state->mstack, state->mstack_alloc);
+ }
+ state->mstack[state->msp] = method;
+ if (method->flags & SMETH_MODIFIER)
+ {
+ return MODIFIER;
+ }
+ switch (method->type)
+ {
+ case INT_VALUE: return METHOD_NUMERIC;
+ case REAL_VALUE: return METHOD_NUMERIC;
+ case POS_VALUE: return METHOD_POS;
+ case GROUP_VALUE: return METHOD_GROUP;
+ default:
+ --state->msp;
+ GMX_ERROR_NORET(gmx::eeInternalError, "Unsupported method type");
+ return INVALID;
+ }
+ }
+ return INVALID; /* Should not be reached */
+}
+
+int
+_gmx_sel_lexer_process_pending(YYSTYPE *yylval, gmx_sel_lexer_t *state)
+{
+ if (state->nextparam)
+ {
+ gmx_ana_selparam_t *param = state->nextparam;
+ gmx_bool bBoolNo = state->bBoolNo;
+
+ if (state->neom > 0)
+ {
+ --state->neom;
+ return END_OF_METHOD;
+ }
+ state->nextparam = NULL;
+ state->bBoolNo = FALSE;
+ _gmx_sel_lexer_add_token(param->name, -1, state);
+ return init_param_token(yylval, param, bBoolNo);
+ }
+ if (state->prev_pos_kw > 0)
+ {
+ --state->prev_pos_kw;
+ }
+ if (state->nextmethod)
+ {
+ gmx_ana_selmethod_t *method = state->nextmethod;
+
+ state->nextmethod = NULL;
+ return init_method_token(yylval, method, TRUE, state);
+ }
+ return 0;
+}
+
+int
+_gmx_sel_lexer_process_identifier(YYSTYPE *yylval, char *yytext, size_t yyleng,
+ gmx_sel_lexer_t *state)
+{
+ gmx_sel_symrec_t *symbol;
+ e_symbol_t symtype;
+
+ /* Check if the identifier matches with a parameter name */
+ if (state->msp >= 0)
+ {
+ gmx_ana_selparam_t *param = NULL;
+ gmx_bool bBoolNo = FALSE;
+ int sp = state->msp;
+ while (!param && sp >= 0)
+ {
+ int i;
+ for (i = 0; i < state->mstack[sp]->nparams; ++i)
+ {
+ /* Skip NULL parameters and too long parameters */
+ if (state->mstack[sp]->param[i].name == NULL
+ || strlen(state->mstack[sp]->param[i].name) > yyleng)
+ {
+ continue;
+ }
+ if (!strncmp(state->mstack[sp]->param[i].name, yytext, yyleng))
+ {
+ param = &state->mstack[sp]->param[i];
+ break;
+ }
+ /* Check separately for a 'no' prefix on gmx_boolean parameters */
+ if (state->mstack[sp]->param[i].val.type == NO_VALUE
+ && yyleng > 2 && yytext[0] == 'n' && yytext[1] == 'o'
+ && !strncmp(state->mstack[sp]->param[i].name, yytext+2, yyleng-2))
+ {
+ param = &state->mstack[sp]->param[i];
+ bBoolNo = TRUE;
+ break;
+ }
+ }
+ if (!param)
+ {
+ --sp;
+ }
+ }
+ if (param)
+ {
+ if (param->val.type == NO_VALUE && !bBoolNo)
+ {
+ state->bMatchBool = TRUE;
+ }
+ if (sp < state->msp)
+ {
+ state->neom = state->msp - sp - 1;
+ state->nextparam = param;
+ state->bBoolNo = bBoolNo;
+ return END_OF_METHOD;
+ }
+ _gmx_sel_lexer_add_token(param->name, -1, state);
+ return init_param_token(yylval, param, bBoolNo);
+ }
+ }
+
+ /* Check if the identifier matches with a symbol */
+ symbol = _gmx_sel_find_symbol_len(state->sc->symtab, yytext, yyleng, FALSE);
+ /* If there is no match, return the token as a string */
+ if (!symbol)
+ {
+ yylval->str = gmx_strndup(yytext, yyleng);
+ _gmx_sel_lexer_add_token(yytext, yyleng, state);
+ return IDENTIFIER;
+ }
+ _gmx_sel_lexer_add_token(_gmx_sel_sym_name(symbol), -1, state);
+ symtype = _gmx_sel_sym_type(symbol);
+ /* Reserved symbols should have been caught earlier */
+ if (symtype == SYMBOL_RESERVED)
+ {
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Mismatch between tokenizer and reserved symbol table");
+ return INVALID;
+ }
+ /* For variable symbols, return the type of the variable value */
+ if (symtype == SYMBOL_VARIABLE)
+ {
+ t_selelem *var;
+
+ var = _gmx_sel_sym_value_var(symbol);
+ /* Return simple tokens for constant variables */
+ if (var->type == SEL_CONST)
+ {
+ switch (var->v.type)
+ {
+ case INT_VALUE:
+ yylval->i = var->v.u.i[0];
+ return TOK_INT;
+ case REAL_VALUE:
+ yylval->r = var->v.u.r[0];
+ return TOK_REAL;
+ case POS_VALUE:
+ break;
+ default:
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Unsupported variable type");
+ return INVALID;
+ }
+ }
+ yylval->sel = var;
+ switch (var->v.type)
+ {
+ case INT_VALUE: return VARIABLE_NUMERIC;
+ case REAL_VALUE: return VARIABLE_NUMERIC;
+ case POS_VALUE: return VARIABLE_POS;
+ case GROUP_VALUE: return VARIABLE_GROUP;
+ default:
+ GMX_ERROR_NORET(gmx::eeInternalError,
+ "Unsupported variable type");
+ return INVALID;
+ }
+ return INVALID; /* Should not be reached. */
+ }
+ /* For method symbols, return the correct type */
+ if (symtype == SYMBOL_METHOD)
+ {
+ gmx_ana_selmethod_t *method;
+
+ method = _gmx_sel_sym_value_method(symbol);
+ return init_method_token(yylval, method, state->prev_pos_kw > 0, state);
+ }
+ /* For position symbols, we need to return KEYWORD_POS, but we also need
+ * some additional handling. */
+ if (symtype == SYMBOL_POS)
+ {
+ state->bMatchOf = TRUE;
+ yylval->str = _gmx_sel_sym_name(symbol);
+ state->prev_pos_kw = 2;
+ return KEYWORD_POS;
+ }
+ /* Should not be reached */
+ return INVALID;
+}
+
+void
+_gmx_sel_lexer_add_token(const char *str, int len, gmx_sel_lexer_t *state)
+{
+ /* Do nothing if the string is empty, or if it is a space and there is
+ * no other text yet, or if there already is a space. */
+ if (!str || len == 0 || strlen(str) == 0
+ || (str[0] == ' ' && str[1] == 0
+ && (state->pslen == 0 || state->pselstr[state->pslen - 1] == ' ')))
+ {
+ return;
+ }
+ if (len < 0)
+ {
+ len = strlen(str);
+ }
+ /* Allocate more memory if necessary */
+ if (state->nalloc_psel - state->pslen < len)
+ {
+ int incr = STRSTORE_ALLOCSTEP < len ? len : STRSTORE_ALLOCSTEP;
+ state->nalloc_psel += incr;
+ srenew(state->pselstr, state->nalloc_psel);
+ }
+ /* Append the token to the stored string */
+ strncpy(state->pselstr + state->pslen, str, len);
+ state->pslen += len;
+ state->pselstr[state->pslen] = 0;
+}
+
+int
+_gmx_sel_init_lexer(yyscan_t *scannerp, struct gmx_ana_selcollection_t *sc,
+ gmx::AbstractErrorReporter *errors,
+ bool bInteractive, int maxnr, bool bGroups,
+ struct gmx_ana_indexgrps_t *grps)
+{
+ gmx_sel_lexer_t *state;
+ int rc;
+
+ rc = _gmx_sel_yylex_init(scannerp);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ snew(state, 1);
+ state->sc = sc;
+ state->errors = errors;
+ state->bGroups = bGroups;
+ state->grps = grps;
+ state->nexpsel = (maxnr > 0 ? sc->sel.size() + maxnr : -1);
+
+ state->bInteractive = bInteractive;
+ state->nalloc_input = 0;
+ state->inputstr = NULL;
+
+ snew(state->pselstr, STRSTORE_ALLOCSTEP);
+ state->pselstr[0] = 0;
+ state->pslen = 0;
+ state->nalloc_psel = STRSTORE_ALLOCSTEP;
+
+ snew(state->mstack, 20);
+ state->mstack_alloc = 20;
+ state->msp = -1;
+ state->neom = 0;
+ state->nextparam = NULL;
+ state->nextmethod = NULL;
+ state->prev_pos_kw = 0;
+ state->bBoolNo = FALSE;
+ state->bMatchOf = FALSE;
+ state->bMatchBool = FALSE;
+ state->bCmdStart = TRUE;
+ state->bBuffer = FALSE;
+
+ _gmx_sel_yyset_extra(state, *scannerp);
+ return 0;
+}
+
+void
+_gmx_sel_free_lexer(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+
+ sfree(state->inputstr);
+ sfree(state->pselstr);
+ sfree(state->mstack);
+ if (state->bBuffer)
+ {
+ _gmx_sel_yy_delete_buffer(state->buffer, scanner);
+ }
+ sfree(state);
+ _gmx_sel_yylex_destroy(scanner);
+}
+
+gmx_bool
+_gmx_sel_is_lexer_interactive(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->bInteractive;
+}
+
+struct gmx_ana_selcollection_t *
+_gmx_sel_lexer_selcollection(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->sc;
+}
+
+gmx::AbstractErrorReporter *
+_gmx_sel_lexer_error_reporter(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->errors;
+}
+
+bool
+_gmx_sel_lexer_has_groups_set(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->bGroups;
+}
+
+struct gmx_ana_indexgrps_t *
+_gmx_sel_lexer_indexgrps(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->grps;
+}
+
+int
+_gmx_sel_lexer_exp_selcount(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->nexpsel;
+}
+
+const char *
+_gmx_sel_lexer_pselstr(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ return state->pselstr;
+}
+
+void
+_gmx_sel_lexer_clear_pselstr(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+ state->pselstr[0] = 0;
+ state->pslen = 0;
+}
+
+void
+_gmx_sel_lexer_clear_method_stack(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+
+ state->msp = -1;
+}
+
+void
+_gmx_sel_finish_method(yyscan_t scanner)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+
+ if (state->msp >= 0)
+ {
+ --state->msp;
+ }
+}
+
+void
+_gmx_sel_set_lex_input_file(yyscan_t scanner, FILE *fp)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+
+ state->bBuffer = TRUE;
+ state->buffer = _gmx_sel_yy_create_buffer(fp, YY_BUF_SIZE, scanner);
+ _gmx_sel_yy_switch_to_buffer(state->buffer, scanner);
+}
+
+void
+_gmx_sel_set_lex_input_str(yyscan_t scanner, const char *str)
+{
+ gmx_sel_lexer_t *state = _gmx_sel_yyget_extra(scanner);
+
+ if (state->bBuffer)
+ {
+ _gmx_sel_yy_delete_buffer(state->buffer, scanner);
+ }
+ state->bBuffer = TRUE;
+ state->buffer = _gmx_sel_yy_scan_string(str, scanner);
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Internal header file used by the selection tokenizer.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_SCANNER_INTERNAL_H
+#define SELECTION_SCANNER_INTERNAL_H
+
+namespace gmx
+{
+class AbstractErrorReporter;
+}
+
+#include "parser.h"
+
+/** The scanning function generated by Flex. */
+#define YY_DECL int _gmx_sel_yylex(YYSTYPE *yylval, yyscan_t yyscanner)
+YY_DECL;
+
+/* These need to be defined before including scanner_flex.h, because it
+ * uses YY_EXTRA_TYPE. But we also need to include it before defining
+ * gmx_sel_lexer_t; hence the forward declaration. */
+struct gmx_sel_lexer_t;
+#define YY_EXTRA_TYPE struct gmx_sel_lexer_t *
+
+/* We cannot include scanner_flex.h from the scanner itself, because it
+ * seems to break everything. */
+/* And we need to define YY_NO_UNISTD_H here as well, otherwise unistd.h
+ * gets included in other files than scanner.cpp... */
+#ifndef FLEX_SCANNER
+#define YY_NO_UNISTD_H
+#include "scanner_flex.h"
+#endif
+
+/*! \internal \brief
+ * Internal data structure for the selection tokenizer state.
+ */
+typedef struct gmx_sel_lexer_t
+{
+ //! Selection collection to put parsed selections in.
+ struct gmx_ana_selcollection_t *sc;
+ //! Error reporter object.
+ gmx::AbstractErrorReporter *errors;
+ //! Whether external index groups have been set.
+ bool bGroups;
+ //! External index groups for resolving \c group keywords.
+ struct gmx_ana_indexgrps_t *grps;
+ //! Number of selections at which the parser should stop.
+ int nexpsel;
+
+ //! Whether the parser is interactive.
+ gmx_bool bInteractive;
+ //! Current input string (line) for an interactive scanner.
+ char *inputstr;
+ //! Number of bytes allocated for \a inputstr.
+ int nalloc_input;
+
+ //! Pretty-printed version of the string parsed since last clear.
+ char *pselstr;
+ //! Length of the string in \a pselstr.
+ int pslen;
+ //! Number of bytes allocated for \a pselstr.
+ int nalloc_psel;
+
+ //! Stack of methods in which parameters should be looked up.
+ struct gmx_ana_selmethod_t **mstack;
+ //! Index of the top of the stack in \a mstack.
+ int msp;
+ //! Number of elements allocated for \a mstack.
+ int mstack_alloc;
+
+ //! Number of END_OF_METHOD tokens to return before \a nextparam.
+ int neom;
+ //! Parameter symbol to return before resuming scanning.
+ struct gmx_ana_selparam_t *nextparam;
+ //! Whether \a nextparam was a boolean parameter with a 'no' prefix.
+ gmx_bool bBoolNo;
+ /*! \brief
+ * Method symbol to return before resuming scanning
+ *
+ * Only used when \p nextparam is NULL.
+ */
+ struct gmx_ana_selmethod_t *nextmethod;
+ //! Used to track whether the previous token was a position modifier.
+ int prev_pos_kw;
+
+ //! Whether the 'of' keyword is acceptable as the next token.
+ gmx_bool bMatchOf;
+ //! Whether boolean values (yes/no/on/off) are acceptable as the next token.
+ gmx_bool bMatchBool;
+ //! Whether the next token starts a new selection.
+ gmx_bool bCmdStart;
+
+ //! Whether an external buffer is set for the scanner.
+ gmx_bool bBuffer;
+ //! The current buffer for the scanner.
+ YY_BUFFER_STATE buffer;
+} gmx_sel_lexer_t;
+
+/* Because Flex defines yylval, yytext, and yyleng as macros,
+ * and this file is included from scanner.l,
+ * we cannot have them here as parameter names... */
+/** Internal function for cases where several tokens need to be returned. */
+int
+_gmx_sel_lexer_process_pending(YYSTYPE *, gmx_sel_lexer_t *state);
+/** Internal function that processes identifier tokens. */
+int
+_gmx_sel_lexer_process_identifier(YYSTYPE *, char *, size_t,
+ gmx_sel_lexer_t *state);
+/** Internal function to add a token to the pretty-printed selection text. */
+void
+_gmx_sel_lexer_add_token(const char *str, int len, gmx_sel_lexer_t *state);
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::Selection.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <smalloc.h>
+#include <statutil.h>
+#include <string2.h>
+#include <xvgr.h>
+
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selvalue.h"
+
+#include "selelem.h"
+
+namespace gmx
+{
+
+Selection::Selection(t_selelem *elem, const char *selstr)
+{
+ _sel.name = strdup(elem->name);
+ _sel.selstr = strdup(selstr);
+ gmx_ana_pos_clear(&_sel.p);
+
+ if (elem->child->type == SEL_CONST)
+ {
+ gmx_ana_pos_copy(&_sel.p, elem->child->v.u.p, TRUE);
+ _sel.bDynamic = FALSE;
+ }
+ else
+ {
+ t_selelem *child;
+
+ child = elem->child;
+ child->flags &= ~SEL_ALLOCVAL;
+ _gmx_selvalue_setstore(&child->v, &_sel.p);
+ /* We should also skip any modifiers to determine the dynamic
+ * status. */
+ while (child->type == SEL_MODIFIER)
+ {
+ child = child->child;
+ if (child->type == SEL_SUBEXPRREF)
+ {
+ child = child->child;
+ /* Because most subexpression elements are created
+ * during compilation, we need to check for them
+ * explicitly here.
+ */
+ if (child->type == SEL_SUBEXPR)
+ {
+ child = child->child;
+ }
+ }
+ }
+ /* For variable references, we should skip the
+ * SEL_SUBEXPRREF and SEL_SUBEXPR elements. */
+ if (child->type == SEL_SUBEXPRREF)
+ {
+ child = child->child->child;
+ }
+ _sel.bDynamic = (child->child->flags & SEL_DYNAMIC);
+ }
+ /* The group will be set after compilation */
+ _sel.m = NULL;
+ _sel.q = NULL;
+ _sel.g = NULL;
+ _sel.orgm = NULL;
+ _sel.orgq = NULL;
+ _sel.selelem = elem;
+ initCoveredFraction(CFRAC_NONE);
+}
+
+
+Selection::~Selection()
+{
+ sfree(_sel.name);
+ sfree(_sel.selstr);
+ gmx_ana_pos_deinit(&_sel.p);
+ if (_sel.m != _sel.orgm)
+ {
+ sfree(_sel.m);
+ }
+ if (_sel.q != _sel.orgq)
+ {
+ sfree(_sel.q);
+ }
+ sfree(_sel.orgm);
+ sfree(_sel.orgq);
+}
+
+
+void
+Selection::printInfo() const
+{
+ fprintf(stderr, "\"%s\" (%d position%s, %d atom%s%s)", _sel.name,
+ _sel.p.nr, _sel.p.nr == 1 ? "" : "s",
+ _sel.g->isize, _sel.g->isize == 1 ? "" : "s",
+ _sel.bDynamic ? ", dynamic" : "");
+ fprintf(stderr, "\n");
+}
+
+
+bool
+Selection::initCoveredFraction(e_coverfrac_t type)
+{
+ gmx_ana_selection_t *sel = &_sel;
+
+ sel->cfractype = type;
+ if (type == CFRAC_NONE || !sel->selelem)
+ {
+ sel->bCFracDyn = FALSE;
+ }
+ else if (!_gmx_selelem_can_estimate_cover(sel->selelem))
+ {
+ sel->cfractype = CFRAC_NONE;
+ sel->bCFracDyn = FALSE;
+ }
+ else
+ {
+ sel->bCFracDyn = TRUE;
+ }
+ sel->cfrac = sel->bCFracDyn ? 0.0 : 1.0;
+ sel->avecfrac = sel->cfrac;
+ return type == CFRAC_NONE || sel->cfractype != CFRAC_NONE;
+}
+
+
+void
+Selection::printDebugInfo(int nmaxind) const
+{
+ fprintf(stderr, " ");
+ printInfo();
+ fprintf(stderr, " ");
+ gmx_ana_index_dump(_sel.g, -1, nmaxind);
+
+ fprintf(stderr, " Block (size=%d):", _sel.p.m.mapb.nr);
+ if (!_sel.p.m.mapb.index)
+ {
+ fprintf(stderr, " (null)");
+ }
+ else
+ {
+ int n = _sel.p.m.mapb.nr;
+ if (nmaxind >= 0 && n > nmaxind)
+ n = nmaxind;
+ for (int i = 0; i <= n; ++i)
+ fprintf(stderr, " %d", _sel.p.m.mapb.index[i]);
+ if (n < _sel.p.m.mapb.nr)
+ fprintf(stderr, " ...");
+ }
+ fprintf(stderr, "\n");
+
+ int n = _sel.p.m.nr;
+ if (nmaxind >= 0 && n > nmaxind)
+ n = nmaxind;
+ fprintf(stderr, " RefId:");
+ if (!_sel.p.m.refid)
+ {
+ fprintf(stderr, " (null)");
+ }
+ else
+ {
+ for (int i = 0; i < n; ++i)
+ fprintf(stderr, " %d", _sel.p.m.refid[i]);
+ if (n < _sel.p.m.nr)
+ fprintf(stderr, " ...");
+ }
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, " MapId:");
+ if (!_sel.p.m.mapid)
+ {
+ fprintf(stderr, " (null)");
+ }
+ else
+ {
+ for (int i = 0; i < n; ++i)
+ fprintf(stderr, " %d", _sel.p.m.mapid[i]);
+ if (n < _sel.p.m.nr)
+ fprintf(stderr, " ...");
+ }
+ fprintf(stderr, "\n");
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::Selection.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTION_H
+#define GMX_SELECTION_SELECTION_H
+
+#include "position.h"
+#include "indexutil.h"
+#include "selectionenums.h"
+
+struct t_selelem;
+
+/*! \internal \brief
+ * Describes a single selection.
+ *
+ * \ingroup module_selection
+ */
+typedef struct gmx_ana_selection_t
+{
+ /** Name of the selection. */
+ char *name;
+ /** The actual selection string. */
+ char *selstr;
+ /** Selected positions. */
+ gmx_ana_pos_t p;
+ /** Masses associated with the positions. */
+ real *m;
+ /** Charges associated with the positions. */
+ real *q;
+ /** Pointer to the index group that holds the selected atoms. */
+ struct gmx_ana_index_t *g;
+ /** TRUE if the value can change as a function of time. */
+ gmx_bool bDynamic;
+ /** Type of the covered fraction. */
+ e_coverfrac_t cfractype;
+ /** TRUE if the covered fraction depends on the frame. */
+ gmx_bool bCFracDyn;
+ /** Covered fraction of the selection for the current frame. */
+ real cfrac;
+ /** The average covered fraction (over the trajectory). */
+ real avecfrac;
+
+ /*! \brief
+ * Pointer to the root of the selection element tree (internal use only).
+ *
+ * \internal
+ * This field is NULL if the selection has been loaded directly from an
+ * index file.
+ */
+ struct t_selelem *selelem;
+ /** Original masses of all possible positions (internal use only). */
+ real *orgm;
+ /** Original charges of all possible positions (internal use only). */
+ real *orgq;
+} gmx_ana_selection_t;
+
+namespace gmx
+{
+
+class SelectionCollection;
+
+/*! \brief
+ * Provides access to a single selection.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class Selection
+{
+ public:
+ /*! \brief
+ * Creates a new selection object.
+ *
+ * \param[in] elem Root of the evaluation tree for this selection.
+ * \param[in] selstr String that was parsed to produce this selection.
+ */
+ Selection(t_selelem *elem, const char *selstr);
+
+ //! Returns the name of the selection.
+ const char *name() const { return _sel.name; }
+ //! Returns the string that was parsed to produce this selection.
+ const char *selectionText() const { return _sel.selstr; }
+ //! Returns true if the size of the selection (posCount()) is dynamic.
+ bool isDynamic() const { return _sel.bDynamic; }
+ //! Returns the type of positions in the selection.
+ e_index_t type() const { return _sel.p.m.type; }
+ //! Number of positions in the selection.
+ int posCount() const { return _sel.p.nr; }
+ //! Returns the \p i'th position for the selection.
+ const rvec &x(int i) const { return _sel.p.x[i]; }
+ //! Returns the velocity for the \p i'th position.
+ const rvec &v(int i) const { return _sel.p.v[i]; }
+ //! Returns the force for the \p i'th position.
+ const rvec &f(int i) const { return _sel.p.f[i]; }
+ /*! \brief
+ * Returns the reference ID for the \p i'th position.
+ */
+ int refId(int i) const { return _sel.p.m.refid[i]; }
+ /*! \brief
+ * Returns the mapped ID for the \p i'th position.
+ */
+ int mapId(int i) const { return _sel.p.m.mapid[i]; }
+ //! Returns the mass for the \p i'th position.
+ real mass(int i) const { return _sel.m[i]; }
+ //! Returns the charge for the \p i'th position.
+ real charge(int i) const { return _sel.q[i]; }
+ //! Returns the number of atoms contributing to the \p i'th position.
+ int atomCount(int i) const
+ { return _sel.p.m.mapb.index[i+1] - _sel.p.m.mapb.index[i]; }
+ //! Returns the atom indices contributing to the \p i'th position.
+ const int *atomIndices(int i) const
+ { return _sel.g ? _sel.g->index + _sel.p.m.mapb.index[i] : NULL; }
+ //! Returns the covered fraction for the current frame.
+ real cfrac() const { return _sel.cfrac; }
+ //! Deprecated method for direct access to position data.
+ const gmx_ana_pos_t *positions() const { return &_sel.p; }
+ //! Deprecated method for direct access to atom index data.
+ gmx_ana_index_t *indexGroup() const { return _sel.g; }
+ //! Deprecated method for direct access to to mapped ID array.
+ int *mapIds() const { return _sel.p.m.mapid; }
+
+ //! Returns true if the given flag is set.
+ bool hasFlag(SelectionFlag flag) const { return _flags.test(flag); }
+ //! Sets the flags for this selection.
+ void setFlags(SelectionFlags flags) { _flags = flags; }
+ /*! \brief
+ * Sets the ID for the \p i'th position for use with mapId().
+ */
+ void setOriginalId(int i, int id) { _sel.p.m.orgid[i] = id; }
+ /*! \brief
+ * Initializes information about covered fractions.
+ *
+ * \param[in] type Type of covered fraction required.
+ * \returns True if the covered fraction can be calculated for the
+ * selection.
+ */
+ bool initCoveredFraction(e_coverfrac_t type);
+
+ //! Prints out one-line description of the selection.
+ void printInfo() const;
+ /*! \brief
+ * Prints out extended information about the selection for debugging.
+ *
+ * \param[in] nmaxind Maximum number of values to print in lists
+ * (-1 = print all).
+ */
+ void printDebugInfo(int nmaxind) const;
+
+ gmx_ana_selection_t _sel;
+
+ private:
+ ~Selection();
+
+ SelectionFlags _flags;
+
+ friend class SelectionCollection;
+
+ // Disallow copy and assign.
+ Selection(const Selection &);
+ void operator =(const Selection &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::SelectionCollection.
+ *
+ * This header also defines ::gmx_ana_selcollection_t, which is used in the old
+ * C code for handling selection collections.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONCOLLECTION_IMPL_H
+#define GMX_SELECTION_SELECTIONCOLLECTION_IMPL_H
+
+#include <string>
+#include <vector>
+
+#include <typedefs.h>
+
+#include "../options/options.h"
+#include "../utility/flags.h"
+#include "indexutil.h"
+#include "selectioncollection.h"
+
+namespace gmx
+{
+class Selection;
+}
+
+/*! \internal \brief
+ * Information for a collection of selections.
+ *
+ * \ingroup module_selection
+ */
+struct gmx_ana_selcollection_t
+{
+ /** Root of the selection element tree. */
+ struct t_selelem *root;
+ /** Array of compiled selections. */
+ std::vector<gmx::Selection *> sel;
+ /** Number of variables defined. */
+ int nvars;
+ /** Selection strings for variables. */
+ char **varstrs;
+
+ /** Topology for the collection. */
+ t_topology *top;
+ /** Index group that contains all the atoms. */
+ struct gmx_ana_index_t gall;
+ /** Position calculation collection used for selection position evaluation. */
+ struct gmx_ana_poscalc_coll_t *pcc;
+ /** Memory pool used for selection evaluation. */
+ struct gmx_sel_mempool_t *mempool;
+ /** Parser symbol table. */
+ struct gmx_sel_symtab_t *symtab;
+};
+
+namespace gmx
+{
+
+class SelectionOptionStorage;
+
+/*! \internal \brief
+ * Private implemention class for SelectionCollection.
+ *
+ * \ingroup module_selection
+ */
+class SelectionCollection::Impl
+{
+ public:
+ struct SelectionRequest
+ {
+ SelectionRequest(const std::string &name, const std::string &descr,
+ int count, SelectionOptionStorage *storage)
+ : name(name), descr(descr), count(count), storage(storage)
+ { }
+
+ std::string name;
+ std::string descr;
+ int count;
+ SelectionOptionStorage *storage;
+ };
+
+ //! Shorthand for a list of selections stored internally.
+ typedef std::vector<Selection *> SelectionList;
+ //! Shorthand for a list of selection requests.
+ typedef std::vector<SelectionRequest> RequestList;
+
+ //! Possible flags for the selection collection.
+ enum Flag
+ {
+ efOwnPositionCollection = 1<<0,
+ efExternalGroupsSet = 1<<1
+ };
+ //! Holds a collection of Flag values.
+ typedef FlagsTemplate<Flag> Flags;
+
+ //! Creates a new selection collection.
+ explicit Impl(gmx_ana_poscalc_coll_t *pcc);
+ ~Impl();
+
+ //! Returns true if the given flag has been set.
+ bool hasFlag(Flag flag) const { return _flags.test(flag); }
+ //! Clears the symbol table of the selection collection.
+ void clearSymbolTable();
+ //! Registers the default selection methods for the collection.
+ int registerDefaultMethods();
+ /*! \brief
+ * Helper function that runs the parser once the tokenizer has been
+ * initialized.
+ *
+ * \param[in,out] scanner Scanner data structure.
+ * \param[in] maxnr Maximum number of selections to parse
+ * (if -1, parse as many as provided by the user).
+ * \param[out] output Vector to which parsed selections are
+ * appended.
+ * \retval 0 on success.
+ * \retval ::eeInvalidInput on error.
+ *
+ * Does not clear \p output.
+ */
+ int runParser(void *scanner, int maxnr,
+ std::vector<Selection *> *output);
+ void requestSelections(const std::string &name,
+ const std::string &descr,
+ int count, SelectionOptionStorage *storage);
+ int resolveExternalGroups(struct t_selelem *root);
+
+ //! Internal data, used for interfacing with old C code.
+ gmx_ana_selcollection_t _sc;
+ //! Options object for setting global properties on the collection.
+ Options _options;
+ //! Default reference position type for selections.
+ std::string _rpost;
+ //! Default output position type for selections.
+ std::string _spost;
+ /*! \brief
+ * Debugging level for the collection.
+ *
+ * Possible values:
+ * - 0: no debugging
+ * - 1: print selection trees after parsing and compilation
+ * - 2: like 1, also print intermediate compilation trees
+ * - 3: like 1, also print the tree after evaluation
+ * - 4: combine 2 and 3
+ */
+ int _debugLevel;
+ //! Flags for various properties of the collection.
+ Flags _flags;
+ //! External index groups (can be NULL).
+ gmx_ana_indexgrps_t *_grps;
+ //! List of selections requested for later parsing.
+ RequestList _requests;
+};
+
+} // namespace gmx
+
+/*! \addtogroup module_selection
+ * \{
+ */
+
+/* In compiler.cpp */
+/*! \internal \brief
+ * Prepares the selections for evaluation and performs some optimizations.
+ */
+int
+gmx_ana_selcollection_compile(gmx::SelectionCollection *coll);
+
+/* In evaluate.cpp */
+/*! \internal \brief
+ * Evaluates the selection.
+ */
+int
+gmx_ana_selcollection_evaluate(gmx_ana_selcollection_t *sc,
+ t_trxframe *fr, t_pbc *pbc);
+/*! \internal \brief
+ * Evaluates the largest possible index groups from dynamic selections.
+ */
+int
+gmx_ana_selcollection_evaluate_fin(gmx_ana_selcollection_t *sc, int nframes);
+
+/*!\}*/
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::SelectionCollection.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <cassert>
+#include <cstdio>
+
+#include <smalloc.h>
+#include <statutil.h>
+#include <string2.h>
+#include <xvgr.h>
+
+#include "poscalc.h"
+#include "selmethod.h"
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectioncollection.h"
+
+#include "mempool.h"
+#include "scanner.h"
+#include "selectioncollection-impl.h"
+#include "selectionoptionstorage.h"
+#include "selelem.h"
+#include "selmethod.h"
+#include "symrec.h"
+
+/* In parser.y */
+/*! \brief
+ * Parser function generated by Bison.
+ */
+int
+_gmx_sel_yybparse(void *scanner);
+
+namespace gmx
+{
+
+/********************************************************************
+ * SelectionCollection::Impl
+ */
+
+SelectionCollection::Impl::Impl(gmx_ana_poscalc_coll_t *pcc)
+ : _options("selection", "Common selection control"),
+ _debugLevel(0), _grps(NULL)
+{
+ _sc.root = NULL;
+ _sc.nvars = 0;
+ _sc.varstrs = NULL;
+ _sc.top = NULL;
+ gmx_ana_index_clear(&_sc.gall);
+ _sc.pcc = pcc;
+ _sc.mempool = NULL;
+ _sc.symtab = NULL;
+}
+
+
+SelectionCollection::Impl::~Impl()
+{
+ _gmx_selelem_free_chain(_sc.root);
+ SelectionList::const_iterator isel;
+ for (isel = _sc.sel.begin(); isel != _sc.sel.end(); ++isel)
+ {
+ delete *isel;
+ }
+ for (int i = 0; i < _sc.nvars; ++i)
+ {
+ sfree(_sc.varstrs[i]);
+ }
+ sfree(_sc.varstrs);
+ gmx_ana_index_deinit(&_sc.gall);
+ if (_sc.mempool)
+ {
+ _gmx_sel_mempool_destroy(_sc.mempool);
+ }
+ if (hasFlag(efOwnPositionCollection))
+ {
+ gmx_ana_poscalc_coll_free(_sc.pcc);
+ }
+ clearSymbolTable();
+}
+
+
+void
+SelectionCollection::Impl::clearSymbolTable()
+{
+ if (_sc.symtab)
+ {
+ _gmx_sel_symtab_free(_sc.symtab);
+ _sc.symtab = NULL;
+ }
+}
+
+
+int
+SelectionCollection::Impl::runParser(yyscan_t scanner, int maxnr,
+ std::vector<Selection *> *output)
+{
+ gmx_ana_selcollection_t *sc = &_sc;
+ assert(sc == _gmx_sel_lexer_selcollection(scanner));
+
+ int oldCount = sc->sel.size();
+ int bOk = !_gmx_sel_yybparse(scanner);
+ _gmx_sel_free_lexer(scanner);
+ int nr = sc->sel.size() - oldCount;
+ if (maxnr > 0 && nr != maxnr)
+ {
+ return eeInvalidInput;
+ }
+
+ if (bOk)
+ {
+ SelectionList::const_iterator i;
+ for (i = _sc.sel.begin() + oldCount; i != _sc.sel.end(); ++i)
+ {
+ output->push_back(*i);
+ }
+ }
+
+ return bOk ? 0 : eeInvalidInput;
+}
+
+
+void SelectionCollection::Impl::requestSelections(
+ const std::string &name, const std::string &descr,
+ int count, SelectionOptionStorage *storage)
+{
+ _requests.push_back(SelectionRequest(name, descr, count, storage));
+}
+
+
+int SelectionCollection::Impl::resolveExternalGroups(t_selelem *root)
+{
+ int rc = 0;
+
+ if (root->type == SEL_GROUPREF)
+ {
+ if (root->u.gref.name != NULL)
+ {
+ char *name = root->u.gref.name;
+ if (!gmx_ana_indexgrps_find(&root->u.cgrp, _grps, name))
+ {
+ // TODO: Improve error messages
+ GMX_ERROR_NORET(eeInvalidInput,
+ "Unknown group referenced in a selection");
+ rc = eeInvalidInput;
+ }
+ else
+ {
+ sfree(name);
+ }
+ }
+ else
+ {
+ if (!gmx_ana_indexgrps_extract(&root->u.cgrp, _grps,
+ root->u.gref.id))
+ {
+ // TODO: Improve error messages
+ GMX_ERROR_NORET(eeInvalidInput,
+ "Unknown group referenced in a selection");
+ rc = eeInvalidInput;
+ }
+ }
+ if (rc == 0)
+ {
+ root->type = SEL_CONST;
+ root->name = root->u.cgrp.name;
+ }
+ }
+
+ t_selelem *child = root->child;
+ while (child != NULL)
+ {
+ int rc1 = resolveExternalGroups(child);
+ rc = (rc == 0 ? rc1 : rc);
+ child = child->next;
+ }
+ return rc;
+}
+
+
+/********************************************************************
+ * SelectionCollection
+ */
+
+SelectionCollection::SelectionCollection(gmx_ana_poscalc_coll_t *pcc)
+ : _impl(new Impl(pcc))
+{
+}
+
+
+SelectionCollection::~SelectionCollection()
+{
+ delete _impl;
+}
+
+
+int
+SelectionCollection::init()
+{
+ if (_impl->_sc.pcc == NULL)
+ {
+ int rc = gmx_ana_poscalc_coll_create(&_impl->_sc.pcc);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ _impl->_flags.set(Impl::efOwnPositionCollection);
+ }
+ _gmx_sel_symtab_create(&_impl->_sc.symtab);
+ gmx_ana_selmethod_register_defaults(_impl->_sc.symtab);
+ return 0;
+}
+
+
+int
+SelectionCollection::create(SelectionCollection **scp,
+ gmx_ana_poscalc_coll_t *pcc)
+{
+ SelectionCollection *sc = new SelectionCollection(pcc);
+
+ int rc = sc->init();
+ if (rc != 0)
+ {
+ *scp = NULL;
+ delete sc;
+ return rc;
+ }
+ *scp = sc;
+ return 0;
+}
+
+
+Options *
+SelectionCollection::initOptions()
+{
+ static const char * const debug_levels[]
+ = {"no", "basic", "compile", "eval", "full", NULL};
+ /*
+ static const char * const desc[] = {
+ "This program supports selections in addition to traditional",
+ "index files. Use [TT]-select help[tt] for additional information,",
+ "or type 'help' in the selection prompt.",
+ NULL,
+ };
+ options.setDescription(desc);
+ */
+
+ Options &options = _impl->_options;
+ const char **postypes = gmx_ana_poscalc_create_type_enum(TRUE);
+ if (postypes == NULL)
+ {
+ return NULL;
+ }
+ options.addOption(StringOption("selrpos").enumValue(postypes + 1)
+ .store(&_impl->_rpost).defaultValue(postypes[1])
+ .description("Selection reference positions"));
+ options.addOption(StringOption("seltype").enumValue(postypes + 1)
+ .store(&_impl->_spost).defaultValue(postypes[1])
+ .description("Default selection output positions"));
+ assert(_impl->_debugLevel >= 0 && _impl->_debugLevel <= 4);
+ options.addOption(StringOption("seldebug").hidden()
+ .enumValue(debug_levels)
+ .defaultValue(debug_levels[_impl->_debugLevel])
+ .storeEnumIndex(&_impl->_debugLevel)
+ .description("Print out selection trees for debugging"));
+ sfree(postypes);
+
+ return &_impl->_options;
+}
+
+
+void
+SelectionCollection::setReferencePosType(const char *type)
+{
+ assert(type != NULL);
+ _impl->_rpost = type;
+}
+
+
+void
+SelectionCollection::setOutputPosType(const char *type)
+{
+ assert(type != NULL);
+ _impl->_spost = type;
+}
+
+
+void
+SelectionCollection::setDebugLevel(int debuglevel)
+{
+ _impl->_debugLevel = debuglevel;
+}
+
+
+int
+SelectionCollection::setTopology(t_topology *top, int natoms)
+{
+ gmx_ana_selcollection_t *sc = &_impl->_sc;
+ gmx_ana_poscalc_coll_set_topology(sc->pcc, top);
+ sc->top = top;
+
+ /* Get the number of atoms from the topology if it is not given */
+ if (natoms <= 0)
+ {
+ if (sc->top == NULL)
+ {
+ GMX_ERROR(eeInvalidValue,
+ "Selections need either the topology or the number of atoms");
+ }
+ natoms = sc->top->atoms.nr;
+ }
+ gmx_ana_index_init_simple(&sc->gall, natoms, NULL);
+ return 0;
+}
+
+
+int
+SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
+{
+ assert(grps == NULL || !_impl->hasFlag(Impl::efExternalGroupsSet));
+ _impl->_grps = grps;
+ _impl->_flags.set(Impl::efExternalGroupsSet);
+
+ int rc = 0;
+ t_selelem *root = _impl->_sc.root;
+ while (root != NULL)
+ {
+ int rc1 = _impl->resolveExternalGroups(root);
+ rc = (rc == 0 ? rc1 : rc);
+ root = root->next;
+ }
+ return rc;
+}
+
+
+bool
+SelectionCollection::requiresTopology() const
+{
+ t_selelem *sel;
+ e_poscalc_t type;
+ int flags;
+ int rc;
+
+ if (!_impl->_rpost.empty())
+ {
+ flags = 0;
+ rc = gmx_ana_poscalc_type_from_enum(_impl->_rpost.c_str(), &type, &flags);
+ if (rc == 0 && type != POS_ATOM)
+ {
+ return TRUE;
+ }
+ }
+ if (!_impl->_spost.empty())
+ {
+ flags = 0;
+ rc = gmx_ana_poscalc_type_from_enum(_impl->_spost.c_str(), &type, &flags);
+ if (rc == 0 && type != POS_ATOM)
+ {
+ return TRUE;
+ }
+ }
+
+ sel = _impl->_sc.root;
+ while (sel)
+ {
+ if (_gmx_selelem_requires_top(sel))
+ {
+ return TRUE;
+ }
+ sel = sel->next;
+ }
+ return FALSE;
+}
+
+
+int
+SelectionCollection::parseRequestedFromStdin(bool bInteractive,
+ AbstractErrorReporter *errors)
+{
+ int rc = 0;
+ Impl::RequestList::const_iterator i;
+ for (i = _impl->_requests.begin(); i != _impl->_requests.end(); ++i)
+ {
+ const Impl::SelectionRequest &request = *i;
+ if (bInteractive)
+ {
+ std::fprintf(stderr, "\nSpecify ");
+ if (request.count < 0)
+ {
+ std::fprintf(stderr, "any number of selections");
+ }
+ else if (request.count == 1)
+ {
+ std::fprintf(stderr, "a selection");
+ }
+ else
+ {
+ std::fprintf(stderr, "%d selections", request.count);
+ }
+ std::fprintf(stderr, " for option '%s' (%s):\n",
+ request.name.c_str(), request.descr.c_str());
+ std::fprintf(stderr, "(one selection per line, 'help' for help%s)\n",
+ request.count < 0 ? ", Ctrl-D to end" : "");
+ }
+ std::vector<Selection *> selections;
+ rc = parseFromStdin(request.count, bInteractive, errors, &selections);
+ if (rc != 0)
+ {
+ break;
+ }
+ rc = request.storage->addSelections(selections, true, errors);
+ if (rc != 0)
+ {
+ break;
+ }
+ }
+ _impl->_requests.clear();
+ return rc;
+}
+
+
+int
+SelectionCollection::parseRequestedFromString(const std::string &str,
+ AbstractErrorReporter *errors)
+{
+ std::vector<Selection *> selections;
+ int rc = parseFromString(str, errors, &selections);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ std::vector<Selection *>::const_iterator first = selections.begin();
+ std::vector<Selection *>::const_iterator last = first;
+ Impl::RequestList::const_iterator i;
+ for (i = _impl->_requests.begin(); i != _impl->_requests.end(); ++i)
+ {
+ const Impl::SelectionRequest &request = *i;
+ if (request.count > 0)
+ {
+ if (selections.end() - first < request.count)
+ {
+ errors->error("Too few selections provided");
+ rc = eeInvalidInput;
+ break;
+ }
+ last = first + request.count;;
+ }
+ else
+ {
+ if (i != _impl->_requests.end() - 1)
+ {
+ GMX_ERROR_NORET(eeInvalidValue,
+ "Request for all selections not the last option");
+ rc = eeInvalidValue;
+ break;
+ }
+ last = selections.end();
+ }
+ std::vector<Selection *> curr(first, last);
+ rc = request.storage->addSelections(curr, true, errors);
+ if (rc != 0)
+ {
+ break;
+ }
+ first = last;
+ }
+ _impl->_requests.clear();
+ if (last != selections.end())
+ {
+ errors->error("Too many selections provided");
+ rc = eeInvalidInput;
+ }
+ return rc;
+}
+
+
+int
+SelectionCollection::parseFromStdin(int nr, bool bInteractive,
+ AbstractErrorReporter *errors,
+ std::vector<Selection *> *output)
+{
+ yyscan_t scanner;
+ int rc;
+
+ rc = _gmx_sel_init_lexer(&scanner, &_impl->_sc, errors, bInteractive, nr,
+ _impl->hasFlag(Impl::efExternalGroupsSet),
+ _impl->_grps);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ /* We don't set the lexer input here, which causes it to use a special
+ * internal implementation for reading from stdin. */
+ return _impl->runParser(scanner, nr, output);
+}
+
+
+int
+SelectionCollection::parseFromFile(const std::string &filename,
+ AbstractErrorReporter *errors,
+ std::vector<Selection *> *output)
+{
+ yyscan_t scanner;
+ FILE *fp;
+ int rc;
+
+ rc = _gmx_sel_init_lexer(&scanner, &_impl->_sc, errors, false, -1,
+ _impl->hasFlag(Impl::efExternalGroupsSet),
+ _impl->_grps);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ fp = ffopen(filename.c_str(), "r");
+ _gmx_sel_set_lex_input_file(scanner, fp);
+ rc = _impl->runParser(scanner, -1, output);
+ ffclose(fp);
+ return rc;
+}
+
+
+int
+SelectionCollection::parseFromString(const std::string &str,
+ AbstractErrorReporter *errors,
+ std::vector<Selection *> *output)
+{
+ yyscan_t scanner;
+ int rc;
+
+ rc = _gmx_sel_init_lexer(&scanner, &_impl->_sc, errors, false, -1,
+ _impl->hasFlag(Impl::efExternalGroupsSet),
+ _impl->_grps);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ _gmx_sel_set_lex_input_str(scanner, str.c_str());
+ return _impl->runParser(scanner, -1, output);
+}
+
+
+int
+SelectionCollection::compile()
+{
+ if (!_impl->hasFlag(Impl::efExternalGroupsSet))
+ {
+ int rc = setIndexGroups(NULL);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (_impl->_debugLevel >= 1)
+ {
+ printTree(stderr, false);
+ }
+ int rc = gmx_ana_selcollection_compile(this);
+ if (rc == 0 && _impl->hasFlag(Impl::efOwnPositionCollection))
+ {
+ if (_impl->_debugLevel >= 1)
+ {
+ std::fprintf(stderr, "\n");
+ printTree(stderr, false);
+ std::fprintf(stderr, "\n");
+ gmx_ana_poscalc_coll_print_tree(stderr, _impl->_sc.pcc);
+ std::fprintf(stderr, "\n");
+ }
+ gmx_ana_poscalc_init_eval(_impl->_sc.pcc);
+ if (_impl->_debugLevel >= 1)
+ {
+ gmx_ana_poscalc_coll_print_tree(stderr, _impl->_sc.pcc);
+ std::fprintf(stderr, "\n");
+ }
+ }
+ return rc;
+}
+
+
+int
+SelectionCollection::evaluate(t_trxframe *fr, t_pbc *pbc)
+{
+ if (_impl->hasFlag(Impl::efOwnPositionCollection))
+ {
+ gmx_ana_poscalc_init_frame(_impl->_sc.pcc);
+ }
+ int rc = gmx_ana_selcollection_evaluate(&_impl->_sc, fr, pbc);
+ if (rc == 0 && _impl->_debugLevel >= 3)
+ {
+ std::fprintf(stderr, "\n");
+ printTree(stderr, true);
+ }
+ return rc;
+}
+
+
+int
+SelectionCollection::evaluateFinal(int nframes)
+{
+ return gmx_ana_selcollection_evaluate_fin(&_impl->_sc, nframes);
+}
+
+
+void
+SelectionCollection::printTree(FILE *fp, bool bValues) const
+{
+ t_selelem *sel;
+
+ sel = _impl->_sc.root;
+ while (sel)
+ {
+ _gmx_selelem_print_tree(fp, sel, bValues, 0);
+ sel = sel->next;
+ }
+}
+
+
+void
+SelectionCollection::printXvgrInfo(FILE *out, output_env_t oenv) const
+{
+ int i;
+
+ if (output_env_get_xvg_format(oenv) != exvgNONE)
+ {
+ gmx_ana_selcollection_t *sc = &_impl->_sc;
+ std::fprintf(out, "# Selections:\n");
+ for (i = 0; i < sc->nvars; ++i)
+ {
+ std::fprintf(out, "# %s\n", sc->varstrs[i]);
+ }
+ for (i = 0; i < (int)sc->sel.size(); ++i)
+ {
+ std::fprintf(out, "# %s\n", sc->sel[i]->_sel.selstr);
+ }
+ std::fprintf(out, "#\n");
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::SelectionCollection.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONCOLLECTION_H
+#define GMX_SELECTION_SELECTIONCOLLECTION_H
+
+#include <string>
+#include <vector>
+
+#include <typedefs.h>
+
+struct gmx_ana_indexgrps_t;
+struct gmx_ana_poscalc_coll_t;
+
+namespace gmx
+{
+
+class AbstractErrorReporter;
+class Options;
+class Selection;
+class SelectionOptionStorage;
+
+/*! \libinternal \brief
+ * Collection of selections.
+ *
+ * Some default values must then be set with
+ * gmx_ana_selcollection_set_refpostype() and
+ * gmx_ana_selcollection_set_outpostype().
+ *
+ * After setting the default values, one or more selections can be parsed
+ * with one or more calls to parseFromStdin(), parseFromFile(), and/or
+ * parseFromString(). After all selections are parsed, the topology must be
+ * set with setTopology() unless requiresTopology() returns false (the topology
+ * can also be set earlier). Once all selections are parsed, they must be
+ * compiled all at once using compile().
+ * After compilation, dynamic selections have the maximum number of atoms they
+ * can evaluate to, but positions have undefined values. evaluate() can be
+ * used to update the selections for a new frame.
+ * evaluateFinal() can be called after all the frames have been processed to
+ * restore the selection values back to the ones they were after compile().
+ *
+ * At any point, requiresTopology() can be called to see whether the
+ * information provided so far requires loading the topology.
+ * printTree() can be used to print the internal representation of the
+ * selections (mostly useful for debugging).
+ *
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+class SelectionCollection
+{
+ public:
+ /*! \brief
+ * Creates an empty selection collection.
+ *
+ * \param[in] pcc Position calculation collection to use for selection
+ * evaluation.
+ *
+ * If \p pcc is NULL, an internal collection is created and managed by
+ * the object.
+ */
+ explicit SelectionCollection(gmx_ana_poscalc_coll_t *pcc);
+ ~SelectionCollection();
+
+ /*! \brief
+ * Initializes the object.
+ *
+ * \retval 0 on success.
+ *
+ * Should be called immediately after construction.
+ */
+ int init();
+ /*! \brief
+ * Initializes options for setting global properties on the collection.
+ *
+ * The return value should not be deleted by the caller.
+ */
+ Options *initOptions();
+
+ /*! \brief
+ * Convenience function that creates a new selection collection and
+ * calls init().
+ *
+ * \param[out] scp Pointer to a newly created selection collection.
+ * \param[in] pcc Position calculation data structure to use for
+ * selection evaluation.
+ * \returns 0 on success.
+ */
+ static int create(SelectionCollection **scp, gmx_ana_poscalc_coll_t *pcc);
+
+ /*! \brief
+ * Sets the default reference position handling for a selection
+ * collection.
+ *
+ * \param[in] type Default selection reference position type
+ * (one of the strings acceptable for gmx_ana_poscalc_type_from_enum()).
+ *
+ * Should be called before calling the parser functions, unless
+ * initOptions() has been called. In the latter case, can still be
+ * used to override the default value and/or the value provided through
+ * the Options object.
+ */
+ void setReferencePosType(const char *type);
+ /*! \brief
+ * Sets the default reference position handling for a selection
+ * collection.
+ *
+ * \param[in] type Default selection output position type
+ * (one of the strings acceptable for gmx_ana_poscalc_type_from_enum()).
+ *
+ * Should be called before calling the parser functions, unless
+ * initOptions() has been called. In the latter case, can still be
+ * used to override the default value and/or the value provided through
+ * the Options object.
+ */
+ void setOutputPosType(const char *type);
+ /*! \brief
+ * Sets the debugging level for the selection collection.
+ */
+ void setDebugLevel(int debuglevel);
+
+ /*! \brief
+ * Returns true if the collection requires topology information for
+ * evaluation.
+ *
+ * \returns true if any selection in the collection requires topology
+ * information.
+ *
+ * Before the parser functions have been called, the return value is
+ * based just on the position types set.
+ * After parser functions have been called, the return value also takes
+ * into account the selection keywords used.
+ */
+ bool requiresTopology() const;
+ /*! \brief
+ * Sets the topology for the collection.
+ *
+ * \param[in] top Topology data.
+ * \param[in] natoms Number of atoms. If <=0, the number of
+ * atoms in the topology is used.
+ * \retval 0 on success.
+ * \retval ::eeInvalidValue if \p top is NULL and \p natoms <= 0.
+ *
+ * The topology is also set for the position calculation collection
+ * associated with the collection.
+ *
+ * \p natoms determines the largest atom index that can be selected by
+ * the selection: even if the topology contains more atoms, they will
+ * not be selected.
+ */
+ int setTopology(t_topology *top, int natoms);
+ /*! \brief
+ * Sets the external index groups to use for the selections.
+ *
+ * Can be called only once with non-NULL \p grps.
+ */
+ int setIndexGroups(gmx_ana_indexgrps_t *grps);
+ /*! \brief
+ * Parses selection(s) from standard input for options not yet
+ * provided.
+ *
+ * \param[in] bInteractive Whether the parser should behave
+ * interactively.
+ * \param[in] errors Error reporter object.
+ *
+ * This method cooperates with SelectionOption to allow interactive
+ * input of missing selections after all options have been processed.
+ * It should be called after the Options::finish() method has been
+ * called on all options that add selections to this collection.
+ */
+ int parseRequestedFromStdin(bool bInteractive,
+ AbstractErrorReporter *errors);
+ /*! \brief
+ * Parses selection(s) from a string for options not yet provided.
+ *
+ * \param[in] str String to parse.
+ * \param[in] errors Error reporter object.
+ *
+ * \see parseRequestedFromStdin()
+ */
+ int parseRequestedFromString(const std::string &str,
+ AbstractErrorReporter *errors);
+ /*! \brief
+ * Parses selection(s) from standard input.
+ *
+ * \param[in] count Number of selections to parse
+ * (if -1, parse as many as provided by the user).
+ * \param[in] bInteractive Whether the parser should behave
+ * interactively.
+ * \param[in] errors Error reporter object.
+ * \param[out] output Vector to which parsed selections are appended.
+ * \retval 0 on success.
+ * \retval ::eeInvalidInput on syntax error (an interactive parser
+ * only returns this if an incorrect number of selections is
+ * provided).
+ *
+ * Parsed selections are appended to \p output without clearing it
+ * first. If parsing fails, \p output is not modified.
+ *
+ * The objects returned in \p output remain valid for the lifetime of
+ * the selection collection, and should not be freed by the user.
+ * Some information about the selections only becomes available once
+ * compile() has been called.
+ */
+ int parseFromStdin(int count, bool bInteractive,
+ AbstractErrorReporter *errors,
+ std::vector<Selection *> *output);
+ /*! \brief
+ * Parses selection(s) from a file.
+ *
+ * \param[in] filename Name of the file to parse selections from.
+ * \param[in] errors Error reporter object.
+ * \param[out] output Vector to which parsed selections are appended.
+ * \retval 0 on success.
+ * \retval ::eeInvalidInput on syntax error.
+ *
+ * Parsed selections are appended to \p output without clearing it
+ * first. If parsing fails, \p output is not modified.
+ *
+ * The objects returned in \p output remain valid for the lifetime of
+ * the selection collection, and should not be freed by the user.
+ * Some information about the selections only becomes available once
+ * compile() has been called.
+ */
+ int parseFromFile(const std::string &filename,
+ AbstractErrorReporter *errors,
+ std::vector<Selection *> *output);
+ /*! \brief
+ * Parses selection(s) from a string.
+ *
+ * \param[in] str String to parse selections from.
+ * \param[in] errors Error reporter object.
+ * \param[out] output Vector to which parsed selections are appended.
+ * \retval 0 on success.
+ * \retval ::eeInvalidInput on syntax error.
+ *
+ * Parsed selections are appended to \p output without clearing it
+ * first. If parsing fails, \p output is not modified.
+ *
+ * The objects returned in \p output remain valid for the lifetime of
+ * the selection collection, and should not be freed by the user.
+ * Some information about the selections only becomes available once
+ * compile() has been called.
+ */
+ int parseFromString(const std::string &str,
+ AbstractErrorReporter *errors,
+ std::vector<Selection *> *output);
+ /*! \brief
+ * Prepares the selections for evaluation and performs optimizations.
+ *
+ * \retval 0 on successful compilation, a non-zero error code on error.
+ *
+ * Before compilation, selections should have been added to the
+ * collection using the parseFrom*() functions.
+ * The compiled selection collection can be passed to evaluate() to
+ * evaluate the selection for a frame.
+ * If an error occurs, the collection is cleared.
+ *
+ * The covered fraction information is initialized to ::CFRAC_NONE for
+ * all selections.
+ */
+ int compile();
+ /*! \brief
+ * Evaluates selections in the collection.
+ *
+ * \param[in] fr Frame for which the evaluation should be carried out.
+ * \param[in] pbc PBC data, or NULL if no PBC should be used.
+ * \returns 0 on successful evaluation, a non-zero error code on error.
+ */
+ int evaluate(t_trxframe *fr, t_pbc *pbc);
+ /*! \brief
+ * Evaluates the largest possible index groups from dynamic selections.
+ *
+ * \param[in] nframes Total number of frames.
+ * \returns 0 on successful evaluation, a non-zero error code on error.
+ */
+ int evaluateFinal(int nframes);
+
+ /*! \brief
+ * Prints a human-readable version of the internal selection element
+ * tree.
+ *
+ * \param[in] fp File handle to receive the output.
+ * \param[in] bValues If true, the evaluated values of selection
+ * elements are printed as well.
+ */
+ void printTree(FILE *fp, bool bValues) const;
+ /*! \brief
+ * Prints the selection strings into an XVGR file as comments.
+ *
+ * \param[in] fp Output file.
+ * \param[in] oenv Output options structure.
+ */
+ void printXvgrInfo(FILE *fp, output_env_t oenv) const;
+
+ class Impl;
+ Impl *_impl;
+
+ private:
+ /*! \brief
+ * Needed for handling delayed selection parsing requests.
+ */
+ friend class SelectionOptionStorage;
+
+ // Disallow copy and assign.
+ SelectionCollection(const SelectionCollection &);
+ void operator =(const SelectionCollection &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares common types used in selections.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONENUMS_H
+#define GMX_SELECTION_SELECTIONENUMS_H
+
+#include "../utility/flags.h"
+
+/*! \brief
+ * Defines the type of covered fraction.
+ *
+ * \inpublicapi
+ */
+typedef enum
+{
+ CFRAC_NONE, /**< No covered fraction (everything covered). */
+ CFRAC_SOLIDANGLE /**< Fraction of a solid (3D) angle covered. */
+} e_coverfrac_t;
+
+namespace gmx
+{
+
+/*! \brief
+ * Flags for options.
+ *
+ * These flags are not part of the public interface, even though they are in an
+ * installed header. They are needed in the implementation of SelectionOption.
+ */
+enum SelectionFlag
+{
+ efOnlyStatic = 1<<0,
+ efOnlyAtoms = 1<<1,
+ //! Whether ::POS_MASKONLY should be used for output position evaluation.
+ efDynamicMask = 1<<2,
+ efDynamicOnlyWhole = 1<<3,
+ //! Whether velocities of output positions should be evaluated.
+ efEvaluateVelocities = 1<<5,
+ //! Whether forces on output positions should be evaluated.
+ efEvaluateForces = 1<<6,
+};
+
+//! Holds a collection of ::SelectionFlag values.
+typedef FlagsTemplate<SelectionFlag> SelectionFlags;
+
+}
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::SelectionOption.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#include "gromacs/selection/selectionoption.h"
+
+#include "selectionoptionstorage.h"
+
+namespace gmx
+{
+
+int SelectionOption::createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const
+{
+ return createOptionStorage<SelectionOption, SelectionOptionStorage>(this, options, storage);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SelectionOption.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONOPTION_H
+#define GMX_SELECTION_SELECTIONOPTION_H
+
+#include "../options/abstractoption.h"
+#include "selectionenums.h"
+
+namespace gmx
+{
+
+class Selection;
+class SelectionOptionStorage;
+
+/*! \brief
+ * Specifies an option that provides selection(s).
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionOption : public OptionTemplate<Selection *, SelectionOption>
+{
+ public:
+ //! Initializes an option with the given name.
+ explicit SelectionOption(const char *name) : MyBase(name)
+ { }
+
+ /*! \brief
+ * Request velocity evaluation for output positions.
+ */
+ MyClass &evaluateVelocities()
+ { _selectionFlags.set(efEvaluateVelocities); return me(); }
+ /*! \brief
+ * Request force evaluation for output positions.
+ */
+ MyClass &evaluateForces()
+ { _selectionFlags.set(efEvaluateForces); return me(); }
+ /*! \brief
+ * Only accept selections that evaluate to atom positions.
+ *
+ * TODO: This option is not yet implemented.
+ */
+ MyClass &onlyAtoms()
+ { _selectionFlags.set(efOnlyAtoms); return me(); }
+ /*! \brief
+ * Only accept static selections for this option.
+ */
+ MyClass &onlyStatic()
+ { _selectionFlags.set(efOnlyStatic); return me(); }
+ /*! \brief
+ * Handle dynamic selections for this option with position masks.
+ *
+ * Sets ::POS_MASKONLY on the positions for this selection.
+ */
+ MyClass &dynamicMask()
+ { _selectionFlags.set(efDynamicMask); return me(); }
+ /*! \brief
+ * Disallow using atom coordinates as the reference positions.
+ *
+ * TODO: This option is not yet implemented.
+ */
+ MyClass &dynamicOnlyWhole()
+ { _selectionFlags.set(efDynamicOnlyWhole); return me(); }
+
+ private:
+ // Disable default value because it is impossible to provide a
+ // Selection object.
+ using MyBase::defaultValue;
+
+ virtual int createDefaultStorage(Options *options,
+ AbstractOptionStorage **storage) const;
+
+ SelectionFlags _selectionFlags;
+
+ /*! \brief
+ * Needed to initialize SelectionOptionStorage from this class without
+ * otherwise unnecessary accessors.
+ */
+ friend class SelectionOptionStorage;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::SelectionOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#include "selectionoptionstorage.h"
+
+#include <cassert>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/errorreporting/abstracterrorreporter.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/globalproperties.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectioncollection.h"
+#include "gromacs/selection/selectionoption.h"
+
+#include "selectioncollection-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * SelectionOptionStorage
+ */
+
+SelectionOptionStorage::SelectionOptionStorage()
+{
+ MyBase::setFlag(efNoDefaultValue);
+ MyBase::setFlag(efConversionMayNotAddValues);
+ MyBase::setFlag(efDontCheckMinimumCount);
+}
+
+
+int SelectionOptionStorage::init(const SelectionOption &settings,
+ Options *options)
+{
+ _selectionFlags = settings._selectionFlags;
+ options->globalProperties().request(eogpSelectionCollection);
+ return MyBase::init(settings, options);
+}
+
+
+std::string SelectionOptionStorage::formatValue(int i) const
+{
+ Selection *sel = values().at(i);
+ return (sel != NULL ? sel->selectionText() : "");
+}
+
+
+int SelectionOptionStorage::addSelections(
+ const std::vector<Selection *> &selections,
+ bool bFullValue, AbstractErrorReporter *errors)
+{
+ if (bFullValue && selections.size() < static_cast<size_t>(minValueCount()))
+ {
+ errors->error("Too few selections provided");
+ return eeInvalidInput;
+ }
+ std::vector<Selection *>::const_iterator i;
+ for (i = selections.begin(); i != selections.end(); ++i)
+ {
+ // TODO: Having this check in the parser would make interactive input
+ // behave better.
+ if (_selectionFlags.test(efOnlyStatic) && (*i)->isDynamic())
+ {
+ errors->error("Dynamic selections not supported");
+ return eeInvalidInput;
+ }
+ (*i)->setFlags(_selectionFlags);
+ int rc = addValue(*i);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+ if (bFullValue)
+ {
+ processValues(selections.size(), true);
+ }
+ return 0;
+}
+
+
+int SelectionOptionStorage::convertValue(const std::string &value,
+ AbstractErrorReporter *errors)
+{
+ SelectionCollection *sc =
+ hostOptions().globalProperties().selectionCollection();
+ assert(sc != NULL);
+
+ std::vector<Selection *> selections;
+ // TODO: Implement reading from a file.
+ int rc = sc->parseFromString(value, errors, &selections);
+ if (rc == 0)
+ {
+ rc = addSelections(selections, false, errors);
+ }
+ return rc;
+}
+
+int SelectionOptionStorage::processSet(int nvalues,
+ AbstractErrorReporter *errors)
+{
+ if (nvalues > 0 && nvalues < minValueCount())
+ {
+ // TODO: Remove the invalid values
+ errors->error("Too few (valid) values provided");
+ return eeInvalidInput;
+ }
+ return MyBase::processSet(nvalues, errors);
+}
+
+int SelectionOptionStorage::processAll(AbstractErrorReporter *errors)
+{
+ if ((hasFlag(efRequired) || hasFlag(efSet)) && valueCount() == 0)
+ {
+ SelectionCollection *sc =
+ hostOptions().globalProperties().selectionCollection();
+ assert(sc != NULL);
+
+ sc->_impl->requestSelections(name(), description(),
+ maxValueCount(), this);
+ setFlag(efSet);
+ }
+ return MyBase::processAll(errors);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::SelectionOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONOPTIONSTORAGE_H
+#define GMX_SELECTION_SELECTIONOPTIONSTORAGE_H
+
+#include "../options/optionstoragetemplate.h"
+#include "selectionenums.h"
+
+namespace gmx
+{
+
+class Selection;
+class SelectionOption;
+
+/*! \internal \brief
+ * Converts, validates, and stores selection values.
+ *
+ * \ingroup module_selection
+ */
+class SelectionOptionStorage : public OptionStorageTemplate<Selection *>
+{
+ public:
+ SelectionOptionStorage();
+
+ /*! \brief
+ * Initializes the storage from option settings.
+ *
+ * \param[in] settings Storage settings.
+ * \param[in] options Options object.
+ * \retval 0 on success.
+ */
+ int init(const SelectionOption &settings, Options *options);
+
+ virtual const char *typeString() const { return "sel"; }
+ virtual std::string formatValue(int i) const;
+
+ /*! \brief
+ * Adds selections to the storage.
+ *
+ * \param[in] selections List of selections to add.
+ * \param[in] bFullValue If true, the provided selections are the full
+ * value of the option, and additional checks are performed.
+ * \param[in] errors Error reporter object.
+ *
+ * This function is used to implement the methods
+ * SelectionCollection::parseRequestedFromStdin() and
+ * SelectionCollection::parseRequestedFromString() (called with
+ * \p bFullValue set to true), as well as internally by the storage
+ * class (called with \p bFullValue set to false).
+ */
+ int addSelections(const std::vector<Selection *> &selections,
+ bool bFullValue, AbstractErrorReporter *errors);
+
+ private:
+ virtual int convertValue(const std::string &value,
+ AbstractErrorReporter *errors);
+ virtual int processSet(int nvalues, AbstractErrorReporter *errors);
+ virtual int processAll(AbstractErrorReporter *errors);
+
+ SelectionFlags _selectionFlags;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in selelem.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <smalloc.h>
+#include <gmx_fatal.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "keywords.h"
+#include "mempool.h"
+#include "selelem.h"
+
+/*!
+ * \param[in] sel Selection for which the string is requested
+ * \returns Pointer to a string that corresponds to \p sel->type.
+ *
+ * The return value points to a string constant and should not be \p free'd.
+ *
+ * The function returns NULL if \p sel->type is not one of the valid values.
+ */
+const char *
+_gmx_selelem_type_str(t_selelem *sel)
+{
+ switch (sel->type)
+ {
+ case SEL_CONST: return "CONST";
+ case SEL_EXPRESSION: return "EXPR";
+ case SEL_BOOLEAN: return "BOOL";
+ case SEL_ARITHMETIC: return "ARITH";
+ case SEL_ROOT: return "ROOT";
+ case SEL_SUBEXPR: return "SUBEXPR";
+ case SEL_SUBEXPRREF: return "REF";
+ case SEL_GROUPREF: return "GROUPREF";
+ case SEL_MODIFIER: return "MODIFIER";
+ }
+ return NULL;
+}
+
+/*!
+ * \param[in] val Value structore for which the string is requested.
+ * \returns Pointer to a string that corresponds to \p val->type,
+ * NULL if the type value is invalid.
+ *
+ * The return value points to a string constant and should not be \p free'd.
+ */
+const char *
+_gmx_sel_value_type_str(gmx_ana_selvalue_t *val)
+{
+ switch (val->type)
+ {
+ case NO_VALUE: return "NONE";
+ case INT_VALUE: return "INT";
+ case REAL_VALUE: return "REAL";
+ case STR_VALUE: return "STR";
+ case POS_VALUE: return "VEC";
+ case GROUP_VALUE: return "GROUP";
+ }
+ return NULL;
+}
+
+/*! \copydoc _gmx_selelem_type_str() */
+const char *
+_gmx_selelem_gmx_boolean_type_str(t_selelem *sel)
+{
+ switch (sel->u.boolt)
+ {
+ case BOOL_NOT: return "NOT"; break;
+ case BOOL_AND: return "AND"; break;
+ case BOOL_OR: return "OR"; break;
+ case BOOL_XOR: return "XOR"; break;
+ }
+ return NULL;
+}
+
+/*!
+ * \param[in] type Type of selection element to allocate.
+ * \returns Pointer to the newly allocated and initialized element.
+ *
+ * \c t_selelem::type is set to \p type,
+ * \c t_selelem::v::type is set to \ref GROUP_VALUE for gmx_boolean and comparison
+ * expressions and \ref NO_VALUE for others,
+ * \ref SEL_ALLOCVAL is set for non-root elements (\ref SEL_ALLOCDATA is also
+ * set for \ref SEL_BOOLEAN elements),
+ * and \c t_selelem::refcount is set to one.
+ * All the pointers are set to NULL.
+ */
+t_selelem *
+_gmx_selelem_create(e_selelem_t type)
+{
+ t_selelem *sel;
+
+ snew(sel, 1);
+ sel->name = NULL;
+ sel->type = type;
+ sel->flags = (type != SEL_ROOT) ? SEL_ALLOCVAL : 0;
+ if (type == SEL_BOOLEAN)
+ {
+ sel->v.type = GROUP_VALUE;
+ sel->flags |= SEL_ALLOCDATA;
+ }
+ else
+ {
+ sel->v.type = NO_VALUE;
+ }
+ _gmx_selvalue_clear(&sel->v);
+ sel->evaluate = NULL;
+ sel->mempool = NULL;
+ sel->child = NULL;
+ sel->next = NULL;
+ sel->refcount = 1;
+
+ return sel;
+}
+
+/*!
+ * \param[in,out] sel Selection element to set the type for.
+ * \param[in] vtype Value type for the selection element.
+ * \returns 0 on success, EINVAL if the value type is invalid.
+ *
+ * If the new type is \ref GROUP_VALUE or \ref POS_VALUE, the
+ * \ref SEL_ALLOCDATA flag is also set.
+ *
+ * This function should only be called at most once for each element,
+ * preferably right after calling _gmx_selelem_create().
+ */
+int
+_gmx_selelem_set_vtype(t_selelem *sel, e_selvalue_t vtype)
+{
+ if (sel->type == SEL_BOOLEAN && vtype != GROUP_VALUE)
+ {
+ gmx_bug("internal error");
+ return EINVAL;
+ }
+ if (sel->v.type != NO_VALUE && vtype != sel->v.type)
+ {
+ gmx_call("_gmx_selelem_set_vtype() called more than once");
+ return EINVAL;
+ }
+ sel->v.type = vtype;
+ if (vtype == GROUP_VALUE || vtype == POS_VALUE)
+ {
+ sel->flags |= SEL_ALLOCDATA;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in,out] sel Selection element to reserve.
+ * \param[in] count Number of values to reserve memory for.
+ * \returns 0 on success or if no memory pool, non-zero on error.
+ *
+ * Reserves memory for the values of \p sel from the \p sel->mempool
+ * memory pool. If no memory pool is set, nothing is done.
+ */
+int
+_gmx_selelem_mempool_reserve(t_selelem *sel, int count)
+{
+ int rc = 0;
+
+ if (!sel->mempool)
+ {
+ return 0;
+ }
+ switch (sel->v.type)
+ {
+ case INT_VALUE:
+ rc = _gmx_sel_mempool_alloc(sel->mempool, (void **)&sel->v.u.i,
+ sizeof(*sel->v.u.i)*count);
+ break;
+
+ case REAL_VALUE:
+ rc = _gmx_sel_mempool_alloc(sel->mempool, (void **)&sel->v.u.r,
+ sizeof(*sel->v.u.r)*count);
+ break;
+
+ case GROUP_VALUE:
+ rc = _gmx_sel_mempool_alloc_group(sel->mempool, sel->v.u.g, count);
+ break;
+
+ default:
+ gmx_incons("mem pooling not implemented for requested type");
+ return -1;
+ }
+ return rc;
+}
+
+/*!
+ * \param[in,out] sel Selection element to release.
+ *
+ * Releases the memory allocated for the values of \p sel from the
+ * \p sel->mempool memory pool. If no memory pool is set, nothing is done.
+ */
+void
+_gmx_selelem_mempool_release(t_selelem *sel)
+{
+ if (!sel->mempool)
+ {
+ return;
+ }
+ switch (sel->v.type)
+ {
+ case INT_VALUE:
+ case REAL_VALUE:
+ _gmx_sel_mempool_free(sel->mempool, sel->v.u.ptr);
+ _gmx_selvalue_setstore(&sel->v, NULL);
+ break;
+
+ case GROUP_VALUE:
+ if (sel->v.u.g)
+ {
+ _gmx_sel_mempool_free_group(sel->mempool, sel->v.u.g);
+ }
+ break;
+
+ default:
+ gmx_incons("mem pooling not implemented for requested type");
+ break;
+ }
+}
+
+/*!
+ * \param[in] sel Selection to free.
+ */
+void
+_gmx_selelem_free_values(t_selelem *sel)
+{
+ int i, n;
+
+ _gmx_selelem_mempool_release(sel);
+ if ((sel->flags & SEL_ALLOCDATA) && sel->v.u.ptr)
+ {
+ /* The number of position/group structures is constant, so the
+ * backup of using sel->v.nr should work for them.
+ * For strings, we report an error if we don't know the allocation
+ * size here. */
+ n = (sel->v.nalloc > 0) ? sel->v.nalloc : sel->v.nr;
+ switch (sel->v.type)
+ {
+ case STR_VALUE:
+ if (sel->v.nalloc == 0)
+ {
+ gmx_bug("SEL_ALLOCDATA should only be set for allocated STR_VALUE values");
+ break;
+ }
+ for (i = 0; i < n; ++i)
+ {
+ sfree(sel->v.u.s[i]);
+ }
+ break;
+ case POS_VALUE:
+ for (i = 0; i < n; ++i)
+ {
+ gmx_ana_pos_deinit(&sel->v.u.p[i]);
+ }
+ break;
+ case GROUP_VALUE:
+ for (i = 0; i < n; ++i)
+ {
+ gmx_ana_index_deinit(&sel->v.u.g[i]);
+ }
+ break;
+ default: /* No special handling for other types */
+ break;
+ }
+ }
+ if (sel->flags & SEL_ALLOCVAL)
+ {
+ sfree(sel->v.u.ptr);
+ }
+ _gmx_selvalue_setstore(&sel->v, NULL);
+ if (sel->type == SEL_SUBEXPRREF && sel->u.param)
+ {
+ sel->u.param->val.u.ptr = NULL;
+ }
+}
+
+/*!
+ * \param[in] method Method to free.
+ * \param[in] mdata Method data to free.
+ */
+void
+_gmx_selelem_free_method(gmx_ana_selmethod_t *method, void *mdata)
+{
+ sel_freefunc free_func = NULL;
+
+ /* Save the pointer to the free function. */
+ if (method && method->free)
+ {
+ free_func = method->free;
+ }
+
+ /* Free the method itself.
+ * Has to be done before freeing the method data, because parameter
+ * values are typically stored in the method data, and here we may
+ * access them. */
+ if (method)
+ {
+ int i, j;
+
+ /* Free the memory allocated for the parameters that are not managed
+ * by the selection method itself. */
+ for (i = 0; i < method->nparams; ++i)
+ {
+ gmx_ana_selparam_t *param = &method->param[i];
+
+ if (param->val.u.ptr)
+ {
+ if (param->val.type == GROUP_VALUE)
+ {
+ for (j = 0; j < param->val.nr; ++j)
+ {
+ gmx_ana_index_deinit(¶m->val.u.g[j]);
+ }
+ }
+ else if (param->val.type == POS_VALUE)
+ {
+ for (j = 0; j < param->val.nr; ++j)
+ {
+ gmx_ana_pos_deinit(¶m->val.u.p[j]);
+ }
+ }
+
+ if (param->val.nalloc > 0)
+ {
+ sfree(param->val.u.ptr);
+ }
+ }
+ }
+ sfree(method->param);
+ sfree(method);
+ }
+ /* Free method data. */
+ if (mdata)
+ {
+ if (free_func)
+ {
+ free_func(mdata);
+ }
+ sfree(mdata);
+ }
+}
+
+/*!
+ * \param[in] sel Selection to free.
+ */
+void
+_gmx_selelem_free_exprdata(t_selelem *sel)
+{
+ if (sel->type == SEL_EXPRESSION || sel->type == SEL_MODIFIER)
+ {
+ _gmx_selelem_free_method(sel->u.expr.method, sel->u.expr.mdata);
+ sel->u.expr.mdata = NULL;
+ sel->u.expr.method = NULL;
+ /* Free position data */
+ if (sel->u.expr.pos)
+ {
+ gmx_ana_pos_free(sel->u.expr.pos);
+ sel->u.expr.pos = NULL;
+ }
+ /* Free position calculation data */
+ if (sel->u.expr.pc)
+ {
+ gmx_ana_poscalc_free(sel->u.expr.pc);
+ sel->u.expr.pc = NULL;
+ }
+ }
+ if (sel->type == SEL_ARITHMETIC)
+ {
+ sfree(sel->u.arith.opstr);
+ sel->u.arith.opstr = NULL;
+ }
+ if (sel->type == SEL_SUBEXPR || sel->type == SEL_ROOT
+ || (sel->type == SEL_CONST && sel->v.type == GROUP_VALUE))
+ {
+ gmx_ana_index_deinit(&sel->u.cgrp);
+ }
+ if (sel->type == SEL_GROUPREF)
+ {
+ sfree(sel->u.gref.name);
+ }
+}
+
+/*!
+ * \param[in] sel Selection to free.
+ *
+ * Decrements \ref t_selelem::refcount "sel->refcount" and frees the
+ * memory allocated for \p sel and all its children if the reference count
+ * reaches zero.
+ */
+void
+_gmx_selelem_free(t_selelem *sel)
+{
+ /* Decrement the reference counter and do nothing if references remain */
+ sel->refcount--;
+ if (sel->refcount > 0)
+ {
+ return;
+ }
+
+ /* Free the children.
+ * Must be done before freeing other data, because the children may hold
+ * references to data in this element. */
+ _gmx_selelem_free_chain(sel->child);
+
+ /* Free value storage */
+ _gmx_selelem_free_values(sel);
+
+ /* Free other storage */
+ _gmx_selelem_free_exprdata(sel);
+
+ /* Free temporary compiler data if present */
+ _gmx_selelem_free_compiler_data(sel);
+
+ sfree(sel);
+}
+
+/*!
+ * \param[in] first First selection to free.
+ *
+ * Frees \p first and all selections accessible through the
+ * \ref t_selelem::next "first->next" pointer.
+ */
+void
+_gmx_selelem_free_chain(t_selelem *first)
+{
+ t_selelem *child, *prev;
+
+ child = first;
+ while (child)
+ {
+ prev = child;
+ child = child->next;
+ _gmx_selelem_free(prev);
+ }
+}
+
+/*!
+ * \param[in] fp File handle to receive the output.
+ * \param[in] sel Root of the selection subtree to print.
+ * \param[in] bValues If TRUE, the evaluated values of selection elements
+ * are printed as well.
+ * \param[in] level Indentation level, starting from zero.
+ */
+void
+_gmx_selelem_print_tree(FILE *fp, t_selelem *sel, gmx_bool bValues, int level)
+{
+ t_selelem *child;
+ int i;
+
+ fprintf(fp, "%*c %s %s", level*2+1, '*',
+ _gmx_selelem_type_str(sel), _gmx_sel_value_type_str(&sel->v));
+ if (sel->name)
+ {
+ fprintf(fp, " \"%s\"", sel->name);
+ }
+ fprintf(fp, " flg=");
+ if (sel->flags & SEL_FLAGSSET)
+ {
+ fprintf(fp, "s");
+ }
+ if (sel->flags & SEL_SINGLEVAL)
+ {
+ fprintf(fp, "S");
+ }
+ if (sel->flags & SEL_ATOMVAL)
+ {
+ fprintf(fp, "A");
+ }
+ if (sel->flags & SEL_VARNUMVAL)
+ {
+ fprintf(fp, "V");
+ }
+ if (sel->flags & SEL_DYNAMIC)
+ {
+ fprintf(fp, "D");
+ }
+ if (!(sel->flags & SEL_VALFLAGMASK))
+ {
+ fprintf(fp, "0");
+ }
+ if (sel->mempool)
+ {
+ fprintf(fp, "P");
+ }
+ if (sel->type == SEL_CONST)
+ {
+ if (sel->v.type == INT_VALUE)
+ {
+ fprintf(fp, " %d", sel->v.u.i[0]);
+ }
+ else if (sel->v.type == REAL_VALUE)
+ {
+ fprintf(fp, " %f", sel->v.u.r[0]);
+ }
+ else if (sel->v.type == GROUP_VALUE)
+ {
+ gmx_ana_index_t *g = sel->v.u.g;
+ if (!g || g->isize == 0)
+ g = &sel->u.cgrp;
+ fprintf(fp, " (%d atoms)", g->isize);
+ }
+ }
+ else if (sel->type == SEL_BOOLEAN)
+ {
+ fprintf(fp, " %s", _gmx_selelem_gmx_boolean_type_str(sel));
+ }
+ else if (sel->type == SEL_EXPRESSION
+ && sel->u.expr.method->name == sm_compare.name)
+ {
+ _gmx_selelem_print_compare_info(fp, sel->u.expr.mdata);
+ }
+ if (sel->evaluate)
+ {
+ fprintf(fp, " eval=");
+ _gmx_sel_print_evalfunc_name(fp, sel->evaluate);
+ }
+ if (sel->refcount > 1)
+ {
+ fprintf(fp, " refc=%d", sel->refcount);
+ }
+ if (!(sel->flags & SEL_ALLOCVAL))
+ {
+ fprintf(fp, " (ext. output)");
+ }
+ fprintf(fp, "\n");
+
+ if ((sel->type == SEL_CONST && sel->v.type == GROUP_VALUE) || sel->type == SEL_ROOT)
+ {
+ gmx_ana_index_t *g = sel->v.u.g;
+ if (!g || g->isize == 0 || sel->evaluate != NULL)
+ {
+ g = &sel->u.cgrp;
+ }
+ if (g->isize < 0)
+ {
+ fprintf(fp, "%*c group: (null)\n", level*2+1, ' ');
+ }
+ else if (g->isize > 0)
+ {
+ fprintf(fp, "%*c group:", level*2+1, ' ');
+ if (g->isize <= 20)
+ {
+ for (i = 0; i < g->isize; ++i)
+ {
+ fprintf(fp, " %d", g->index[i] + 1);
+ }
+ }
+ else
+ {
+ fprintf(fp, " %d atoms", g->isize);
+ }
+ fprintf(fp, "\n");
+ }
+ }
+ else if (sel->type == SEL_EXPRESSION)
+ {
+ if (sel->u.expr.pc)
+ {
+ fprintf(fp, "%*c COM", level*2+3, '*');
+ fprintf(fp, "\n");
+ }
+ }
+
+ if (sel->cdata)
+ {
+ _gmx_selelem_print_compiler_info(fp, sel, level);
+ }
+
+ if (bValues && sel->type != SEL_CONST && sel->type != SEL_ROOT && sel->v.u.ptr)
+ {
+ fprintf(fp, "%*c value: ", level*2+1, ' ');
+ switch (sel->v.type)
+ {
+ case POS_VALUE:
+ /* In normal use, the pointer should never be NULL, but it's
+ * useful to have the check for debugging to avoid accidental
+ * segfaults when printing the selection tree. */
+ if (sel->v.u.p->x)
+ {
+ fprintf(fp, "(%f, %f, %f)",
+ sel->v.u.p->x[0][XX], sel->v.u.p->x[0][YY],
+ sel->v.u.p->x[0][ZZ]);
+ }
+ else
+ {
+ fprintf(fp, "(null)");
+ }
+ break;
+ case GROUP_VALUE:
+ fprintf(fp, "%d atoms", sel->v.u.g->isize);
+ if (sel->v.u.g->isize < 20)
+ {
+ if (sel->v.u.g->isize > 0)
+ {
+ fprintf(fp, ":");
+ }
+ for (i = 0; i < sel->v.u.g->isize; ++i)
+ {
+ fprintf(fp, " %d", sel->v.u.g->index[i] + 1);
+ }
+ }
+ break;
+ default:
+ fprintf(fp, "???");
+ break;
+ }
+ fprintf(fp, "\n");
+ }
+
+ /* Print the subexpressions with one more level of indentation */
+ child = sel->child;
+ while (child)
+ {
+ if (!(sel->type == SEL_SUBEXPRREF && child->type == SEL_SUBEXPR))
+ {
+ _gmx_selelem_print_tree(fp, child, bValues, level+1);
+ }
+ child = child->next;
+ }
+}
+
+/*!
+ * \param[in] root Root of the subtree to query.
+ * \returns TRUE if \p root or any any of its elements require topology
+ * information, FALSE otherwise.
+ */
+gmx_bool
+_gmx_selelem_requires_top(t_selelem *root)
+{
+ t_selelem *child;
+
+ if (root->type == SEL_EXPRESSION || root->type == SEL_MODIFIER)
+ {
+ if (root->u.expr.method && (root->u.expr.method->flags & SMETH_REQTOP))
+ {
+ return TRUE;
+ }
+ if (root->u.expr.pc && gmx_ana_poscalc_requires_top(root->u.expr.pc))
+ {
+ return TRUE;
+ }
+ }
+ child = root->child;
+ while (child)
+ {
+ if (_gmx_selelem_requires_top(child))
+ {
+ return TRUE;
+ }
+ child = child->next;
+ }
+ return FALSE;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares ::t_selelem and related things.
+ *
+ * The selection element trees constructed by the parser and the compiler
+ * are described on the respective pages:
+ * \ref page_module_selection_parser and \ref page_module_selection_compiler.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_ELEMENT_H
+#define SELECTION_ELEMENT_H
+
+#include <types/simple.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/selvalue.h"
+
+struct gmx_ana_poscalc_t;
+struct gmx_ana_selparam_t;
+struct gmx_ana_selmethod_t;
+
+struct gmx_sel_evaluate_t;
+struct gmx_sel_mempool_t;
+struct t_selelem;
+
+/********************************************************************/
+/*! \name Enumerations for expression types
+ ********************************************************************/
+/*@{*/
+
+/** Defines the type of a \c t_selelem object. */
+typedef enum
+{
+ /** Constant-valued expression. */
+ SEL_CONST,
+ /** Method expression that requires evaluation. */
+ SEL_EXPRESSION,
+ /** Boolean expression. */
+ SEL_BOOLEAN,
+ /** Arithmetic expression. */
+ SEL_ARITHMETIC,
+ /** Root node of the evaluation tree. */
+ SEL_ROOT,
+ /** Subexpression that may be referenced several times. */
+ SEL_SUBEXPR,
+ /** Reference to a subexpression. */
+ SEL_SUBEXPRREF,
+ /** Unresolved reference to an external group. */
+ SEL_GROUPREF,
+ /** Post-processing of selection value. */
+ SEL_MODIFIER
+} e_selelem_t;
+
+/** Defines the gmx_boolean operation of \c t_selelem objects with type \ref SEL_BOOLEAN. */
+typedef enum
+{
+ BOOL_NOT, /**< Not */
+ BOOL_AND, /**< And */
+ BOOL_OR, /**< Or */
+ BOOL_XOR /**< Xor (not implemented). */
+} e_boolean_t;
+
+/** Defines the arithmetic operation of \c t_selelem objects with type \ref SEL_ARITHMETIC. */
+typedef enum
+{
+ ARITH_PLUS, /**< + */
+ ARITH_MINUS, /**< - */
+ ARITH_NEG, /**< Unary - */
+ ARITH_MULT, /**< * */
+ ARITH_DIV, /**< / */
+ ARITH_EXP /**< ^ (to power) */
+} e_arithmetic_t;
+
+/** Returns a string representation of the type of a \c t_selelem. */
+extern const char *
+_gmx_selelem_type_str(struct t_selelem *sel);
+/** Returns a string representation of the gmx_boolean type of a \ref SEL_BOOLEAN \c t_selelem. */
+extern const char *
+_gmx_selelem_gmx_boolean_type_str(struct t_selelem *sel);
+/** Returns a string representation of the type of a \c gmx_ana_selvalue_t. */
+extern const char *
+_gmx_sel_value_type_str(gmx_ana_selvalue_t *val);
+
+/*@}*/
+
+
+/********************************************************************/
+/*! \name Selection expression flags
+ * \anchor selelem_flags
+ ********************************************************************/
+/*@{*/
+/*! \brief
+ * Selection value flags are set.
+ *
+ * If this flag is set, the flags covered by \ref SEL_VALFLAGMASK
+ * have been set properly for the element.
+ */
+#define SEL_FLAGSSET 1
+/*! \brief
+ * The element evaluates to a single value.
+ *
+ * This flag is always set for \ref GROUP_VALUE elements.
+ */
+#define SEL_SINGLEVAL 2
+/*! \brief
+ * The element evaluates to one value for each input atom.
+ */
+#define SEL_ATOMVAL 4
+/*! \brief
+ * The element evaluates to an arbitrary number of values.
+ */
+#define SEL_VARNUMVAL 8
+/*! \brief
+ * The element (or one of its children) is dynamic.
+ */
+#define SEL_DYNAMIC 16
+/*! \brief
+ * Mask that covers the flags that describe the number of values.
+ */
+#define SEL_VALTYPEMASK (SEL_SINGLEVAL | SEL_ATOMVAL | SEL_VARNUMVAL)
+/*! \brief
+ * Mask that covers the flags that describe the value type.
+ */
+#define SEL_VALFLAGMASK (SEL_FLAGSSET | SEL_VALTYPEMASK | SEL_DYNAMIC)
+/*! \brief
+ * Data has been allocated for the \p v.u union.
+ *
+ * If not set, the \p v.u.ptr points to data allocated externally.
+ * This is the case if the value of the element is used as a parameter
+ * for a selection method or if the element evaluates the final value of
+ * a selection.
+ *
+ * Even if the flag is set, \p v.u.ptr can be NULL during initialization.
+ *
+ * \todo
+ * This flag overlaps with the function of \p v.nalloc field, and could
+ * probably be removed, making memory management simpler. Currently, the
+ * \p v.nalloc field is not kept up-to-date in all cases when this flag
+ * is changed and is used in places where this flag is not, so this would
+ * require a careful investigation of the selection code.
+ */
+#define SEL_ALLOCVAL (1<<8)
+/*! \brief
+ * Data has been allocated for the group/position structure.
+ *
+ * If not set, the memory allocated for fields in \p v.u.g or \p v.u.p is
+ * managed externally.
+ *
+ * This field has no effect if the value type is not \ref GROUP_VALUE or
+ * \ref POS_VALUE, but should not be set.
+ */
+#define SEL_ALLOCDATA (1<<9)
+/*! \brief
+ * \p method->init_frame should be called for the frame.
+ */
+#define SEL_INITFRAME (1<<10)
+/*! \brief
+ * Parameter has been evaluated for the current frame.
+ *
+ * This flag is set for children of \ref SEL_EXPRESSION elements (which
+ * describe method parameters) after the element has been evaluated for the
+ * current frame.
+ * It is not set for \ref SEL_ATOMVAL elements, because they may need to
+ * be evaluated multiple times.
+ */
+#define SEL_EVALFRAME (1<<11)
+/*! \brief
+ * \p method->init has been called.
+ */
+#define SEL_METHODINIT (1<<12)
+/*! \brief
+ * \p method->outinit has been called.
+ *
+ * This flag is also used for \ref SEL_SUBEXPRREF elements.
+ */
+#define SEL_OUTINIT (1<<13)
+/*@}*/
+
+
+/********************************************************************/
+/*! \name Selection expression data structures and functions
+ ********************************************************************/
+/*@{*/
+
+struct t_selelem;
+
+/*! \brief
+ * Function pointer for evaluating a \c t_selelem.
+ */
+typedef int (*sel_evalfunc)(struct gmx_sel_evaluate_t *data,
+ struct t_selelem *sel, gmx_ana_index_t *g);
+
+/*! \internal \brief
+ * Represents an element of a selection expression.
+ */
+typedef struct t_selelem
+{
+ /*! \brief Name of the element.
+ *
+ * This field is only used for informative purposes.
+ * It is always either NULL or a pointer to a string.
+ * Memory is never allocated for it directly.
+ */
+ const char *name;
+ /** Type of the element. */
+ e_selelem_t type;
+ /*! \brief
+ * Value storage of the element.
+ *
+ * This field contains the evaluated value of the element, as well as
+ * the output value type.
+ */
+ gmx_ana_selvalue_t v;
+ /*! \brief
+ * Evaluation function for the element.
+ *
+ * Can be either NULL (if the expression is a constant and does not require
+ * evaluation) or point to one of the functions defined in evaluate.h.
+ */
+ sel_evalfunc evaluate;
+ /*! \brief
+ * Information flags about the element.
+ *
+ * Allowed flags are listed here:
+ * \ref selelem_flags "flags for \c t_selelem".
+ */
+ int flags;
+ /** Data required by the evaluation function. */
+ union {
+ /*! \brief Index group data for several element types.
+ *
+ * - \ref SEL_CONST : if the value type is \ref GROUP_VALUE,
+ * this field holds the unprocessed group value.
+ * - \ref SEL_ROOT : holds the group value for which the
+ * selection subtree should be evaluated.
+ * - \ref SEL_SUBEXPR : holds the group for which the subexpression
+ * has been evaluated.
+ */
+ gmx_ana_index_t cgrp;
+ /** Data for \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements. */
+ struct {
+ /** Pointer the the method used in this expression. */
+ struct gmx_ana_selmethod_t *method;
+ /** Pointer to the data allocated by the method's \p init_data (see sel_datafunc()). */
+ void *mdata;
+ /** Pointer to the position data passed to the method. */
+ struct gmx_ana_pos_t *pos;
+ /** Pointer to the evaluation data for \p pos. */
+ struct gmx_ana_poscalc_t *pc;
+ } expr;
+ /** Operation type for \ref SEL_BOOLEAN elements. */
+ e_boolean_t boolt;
+ /** Operation type for \ref SEL_ARITHMETIC elements. */
+ struct {
+ /** Operation type. */
+ e_arithmetic_t type;
+ /** String representation. */
+ char *opstr;
+ } arith;
+ /** Associated selection parameter for \ref SEL_SUBEXPRREF elements. */
+ struct gmx_ana_selparam_t *param;
+ /** The string/number used to reference the group. */
+ struct {
+ /** Name of the referenced external group. */
+ char *name;
+ /** If \a name is NULL, the index number of the referenced group. */
+ int id;
+ } gref;
+ } u;
+ /** Memory pool to use for values, or NULL if standard memory handling. */
+ struct gmx_sel_mempool_t *mempool;
+ /** Internal data for the selection compiler. */
+ struct t_compiler_data *cdata;
+
+ /*! \brief The first child element.
+ *
+ * Other children can be accessed through the \p next field of \p child.
+ */
+ struct t_selelem *child;
+ /** The next sibling element. */
+ struct t_selelem *next;
+ /*! \brief Number of references to this element.
+ *
+ * Should be larger than one only for \ref SEL_SUBEXPR elements.
+ */
+ int refcount;
+} t_selelem;
+
+/* In evaluate.c */
+/** Writes out a human-readable name for an evaluation function. */
+extern void
+_gmx_sel_print_evalfunc_name(FILE *fp, sel_evalfunc evalfunc);
+
+/** Allocates memory and performs some common initialization for a \c t_selelem. */
+extern t_selelem *
+_gmx_selelem_create(e_selelem_t type);
+/** Sets the value type of a \c t_selelem. */
+extern int
+_gmx_selelem_set_vtype(t_selelem *sel, e_selvalue_t vtype);
+/** Reserves memory for value of a \c t_selelem from a memory pool. */
+extern int
+_gmx_selelem_mempool_reserve(t_selelem *sel, int count);
+/** Releases memory pool used for value of a \c t_selelem. */
+extern void
+_gmx_selelem_mempool_release(t_selelem *sel);
+/** Frees the memory allocated for a \c t_selelem structure and all its children. */
+extern void
+_gmx_selelem_free(t_selelem *sel);
+/** Frees the memory allocated for a \c t_selelem structure, all its children, and also all structures referenced through t_selelem::next fields. */
+extern void
+_gmx_selelem_free_chain(t_selelem *first);
+
+/** Frees the memory allocated for the \c t_selelem::d union. */
+extern void
+_gmx_selelem_free_values(t_selelem *sel);
+/** Frees the memory allocated for a selection method. */
+extern void
+_gmx_selelem_free_method(struct gmx_ana_selmethod_t *method, void *mdata);
+/** Frees the memory allocated for the \c t_selelem::u field. */
+extern void
+_gmx_selelem_free_exprdata(t_selelem *sel);
+/* In compiler.c */
+/** Frees the memory allocated for the selection compiler. */
+extern void
+_gmx_selelem_free_compiler_data(t_selelem *sel);
+
+/** Prints a human-readable version of a selection element subtree. */
+extern void
+_gmx_selelem_print_tree(FILE *fp, t_selelem *root, gmx_bool bValues, int level);
+/* In compile.c */
+/** Prints a human-readable version of the internal compiler data structure. */
+extern void
+_gmx_selelem_print_compiler_info(FILE *fp, t_selelem *sel, int level);
+
+/** Returns TRUE if the selection element subtree requires topology information for evaluation. */
+extern gmx_bool
+_gmx_selelem_requires_top(t_selelem *root);
+
+/* In sm_insolidangle.c */
+/** Returns TRUE if the covered fraction of the selection can be calculated. */
+extern gmx_bool
+_gmx_selelem_can_estimate_cover(t_selelem *sel);
+/** Returns the covered fraction of the selection for the current frame. */
+extern real
+_gmx_selelem_estimate_coverfrac(t_selelem *sel);
+
+/*@}*/
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in selhelp.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <macros.h>
+#include <string2.h>
+#include <wman.h>
+
+#include "gromacs/selection/selmethod.h"
+
+#include "selhelp.h"
+#include "symrec.h"
+
+/*! \internal \brief
+ * Describes a selection help section.
+ */
+typedef struct {
+ //! Topic keyword that produces this help.
+ const char *topic;
+ //! Number of items in the \a text array.
+ int nl;
+ //! Help text as a list of strings that will be concatenated.
+ const char **text;
+} t_selection_help_item;
+
+static const char *help_common[] = {
+ "SELECTION HELP[PAR]",
+
+ "This program supports selections in addition to traditional index files.",
+ "Please read the subtopic pages (available through \"help topic\") for",
+ "more information.",
+ "Explanation of command-line arguments for specifying selections can be",
+ "found under the \"cmdline\" subtopic, and general selection syntax is",
+ "described under \"syntax\". Available keywords can be found under",
+ "\"keywords\", and concrete examples under \"examples\".",
+ "Other subtopics give more details on certain aspects.",
+ "\"help all\" prints the help for all subtopics.",
+};
+
+static const char *help_arithmetic[] = {
+ "ARITHMETIC EXPRESSIONS IN SELECTIONS[PAR]",
+
+ "Basic arithmetic evaluation is supported for numeric expressions.",
+ "Supported operations are addition, subtraction, negation, multiplication,",
+ "division, and exponentiation (using ^).",
+ "Result of a division by zero or other illegal operations is undefined.",
+};
+
+static const char *help_cmdline[] = {
+ "SELECTION COMMAND-LINE ARGUMENTS[PAR]",
+
+ "There are two alternative command-line arguments for specifying",
+ "selections:[BR]",
+ "1. [TT]-select[tt] can be used to specify the complete selection as a",
+ "string on the command line.[BR]",
+ "2. [TT]-sf[tt] can be used to specify a file name from which the",
+ "selection is read.[BR]",
+ "If both options are specified, [TT]-select[tt] takes precedence.",
+ "If neither of the above is present, the user is prompted to type the",
+ "selection on the standard input (a pipe can also be used to provide",
+ "the selections in this case).",
+ "This is also done if an empty string is passed to [TT]-select[tt].[PAR]",
+
+ "Option [TT]-n[tt] can be used to provide an index file.",
+ "If no index file is provided, default groups are generated.",
+ "In both cases, the user can also select an index group instead of",
+ "writing a full selection.",
+ "The default groups are generated by reading selections from a file",
+ "[TT]defselection.dat[tt]. If such a file is found in the current",
+ "directory, it is used instead of the one provided by default.[PAR]",
+
+ "Depending on the tool, two additional command-line arguments may be",
+ "available to control the behavior:[BR]",
+ "1. [TT]-seltype[tt] can be used to specify the default type of",
+ "positions to calculate for each selection.[BR]",
+ "2. [TT]-selrpos[tt] can be used to specify the default type of",
+ "positions used in selecting atoms by coordinates.[BR]",
+ "See \"help positions\" for more information on these options.",
+};
+
+static const char *help_eval[] = {
+ "SELECTION EVALUATION AND OPTIMIZATION[PAR]",
+
+ "Boolean evaluation proceeds from left to right and is short-circuiting",
+ "i.e., as soon as it is known whether an atom will be selected, the",
+ "remaining expressions are not evaluated at all.",
+ "This can be used to optimize the selections: you should write the",
+ "most restrictive and/or the most inexpensive expressions first in",
+ "boolean expressions.",
+ "The relative ordering between dynamic and static expressions does not",
+ "matter: all static expressions are evaluated only once, before the first",
+ "frame, and the result becomes the leftmost expression.[PAR]",
+
+ "Another point for optimization is in common subexpressions: they are not",
+ "automatically recognized, but can be manually optimized by the use of",
+ "variables. This can have a big impact on the performance of complex",
+ "selections, in particular if you define several index groups like this:",
+ " [TT]rdist = distance from com of resnr 1 to 5;[tt][BR]",
+ " [TT]resname RES and rdist < 2;[tt][BR]",
+ " [TT]resname RES and rdist < 4;[tt][BR]",
+ " [TT]resname RES and rdist < 6;[tt][BR]",
+ "Without the variable assignment, the distances would be evaluated three",
+ "times, although they are exactly the same within each selection.",
+ "Anything assigned into a variable becomes a common subexpression that",
+ "is evaluated only once during a frame.",
+ "Currently, in some cases the use of variables can actually lead to a small",
+ "performance loss because of the checks necessary to determine for which",
+ "atoms the expression has already been evaluated, but this should not be",
+ "a major problem.",
+};
+
+static const char *help_examples[] = {
+ "SELECTION EXAMPLES[PAR]",
+
+ "Below, examples of increasingly complex selections are given.[PAR]",
+
+ "Selection of all water oxygens:[BR]",
+ " resname SOL and name OW",
+ "[PAR]",
+
+ "Centers of mass of residues 1 to 5 and 10:[BR]",
+ " res_com of resnr 1 to 5 10",
+ "[PAR]",
+
+ "All atoms farther than 1 nm of a fixed position:[BR]",
+ " not within 1 of (1.2, 3.1, 2.4)",
+ "[PAR]",
+
+ "All atoms of a residue LIG within 0.5 nm of a protein (with a custom name):[BR]",
+ " \"Close to protein\" resname LIG and within 0.5 of group \"Protein\"",
+ "[PAR]",
+
+ "All protein residues that have at least one atom within 0.5 nm of a residue LIG:[BR]",
+ " group \"Protein\" and same residue as within 0.5 of resname LIG",
+ "[PAR]",
+
+ "All RES residues whose COM is between 2 and 4 nm from the COM of all of them:[BR]",
+ " rdist = res_com distance from com of resname RES[BR]",
+ " resname RES and rdist >= 2 and rdist <= 4",
+ "[PAR]",
+
+ "Selection like C1 C2 C2 C3 C3 C4 ... C8 C9 (e.g., for g_bond):[BR]",
+ " name \"C[1-8]\" merge name \"C[2-9]\"",
+};
+
+static const char *help_keywords[] = {
+ "SELECTION KEYWORDS[PAR]",
+
+ "The following selection keywords are currently available.",
+ "For keywords marked with a star, additional help is available through",
+ "\"help KEYWORD\", where KEYWORD is the name of the keyword.",
+};
+
+static const char *help_limits[] = {
+ "SELECTION LIMITATIONS[PAR]",
+
+ "Some analysis programs may require a special structure for the input",
+ "selections (e.g., [TT]g_angle[tt] requires the index group to be made",
+ "of groups of three or four atoms).",
+ "For such programs, it is up to the user to provide a proper selection",
+ "expression that always returns such positions.",
+ "[PAR]",
+
+ "Due to technical reasons, having a negative value as the first value in",
+ "expressions like[BR]",
+ "[TT]charge -1 to -0.7[tt][BR]",
+ "result in a syntax error. A workaround is to write[BR]",
+ "[TT]charge {-1 to -0.7}[tt][BR]",
+ "instead.",
+};
+
+static const char *help_positions[] = {
+ "SPECIFYING POSITIONS[PAR]",
+
+ "Possible ways of specifying positions in selections are:[PAR]",
+
+ "1. A constant position can be defined as [TT][XX, YY, ZZ][tt], where",
+ "[TT]XX[tt], [TT]YY[tt] and [TT]ZZ[tt] are real numbers.[PAR]",
+
+ "2. [TT]com of ATOM_EXPR [pbc][tt] or [TT]cog of ATOM_EXPR [pbc][tt]",
+ "calculate the center of mass/geometry of [TT]ATOM_EXPR[tt]. If",
+ "[TT]pbc[tt] is specified, the center is calculated iteratively to try",
+ "to deal with cases where [TT]ATOM_EXPR[tt] wraps around periodic",
+ "boundary conditions.[PAR]",
+
+ "3. [TT]POSTYPE of ATOM_EXPR[tt] calculates the specified positions for",
+ "the atoms in [TT]ATOM_EXPR[tt].",
+ "[TT]POSTYPE[tt] can be [TT]atom[tt], [TT]res_com[tt], [TT]res_cog[tt],",
+ "[TT]mol_com[tt] or [TT]mol_cog[tt], with an optional prefix [TT]whole_[tt]",
+ "[TT]part_[tt] or [TT]dyn_[tt].",
+ "[TT]whole_[tt] calculates the centers for the whole residue/molecule,",
+ "even if only part of it is selected.",
+ "[TT]part_[tt] prefix calculates the centers for the selected atoms, but",
+ "uses always the same atoms for the same residue/molecule. The used atoms",
+ "are determined from the the largest group allowed by the selection.",
+ "[TT]dyn_[tt] calculates the centers strictly only for the selected atoms.",
+ "If no prefix is specified, whole selections default to [TT]part_[tt] and",
+ "other places default to [TT]whole_[tt].",
+ "The latter is often desirable to select the same molecules in different",
+ "tools, while the first is a compromise between speed ([TT]dyn_[tt]",
+ "positions can be slower to evaluate than [TT]part_[tt]) and intuitive",
+ "behavior.[PAR]",
+
+ "4. [TT]ATOM_EXPR[tt], when given for whole selections, is handled as 3.",
+ "above, using the position type from the command-line argument",
+ "[TT]-seltype[tt].[PAR]",
+
+ "Selection keywords that select atoms based on their positions, such as",
+ "[TT]dist from[tt], use by default the positions defined by the",
+ "[TT]-selrpos[tt] command-line option.",
+ "This can be overridden by prepending a [TT]POSTYPE[tt] specifier to the",
+ "keyword. For example, [TT]res_com dist from POS[tt] evaluates the",
+ "residue center of mass distances. In the example, all atoms of a residue",
+ "are either selected or not, based on the single distance calculated.",
+};
+
+static const char *help_syntax[] = {
+ "SELECTION SYNTAX[PAR]",
+
+ "A set of selections consists of one or more selections, separated by",
+ "semicolons. Each selection defines a set of positions for the analysis.",
+ "Each selection can also be preceded by a string that gives a name for",
+ "the selection for use in, e.g., graph legends.",
+ "If no name is provided, the string used for the selection is used",
+ "automatically as the name.[PAR]",
+
+ "For interactive input, the syntax is slightly altered: line breaks can",
+ "also be used to separate selections. \\ followed by a line break can",
+ "be used to continue a line if necessary.",
+ "Notice that the above only applies to real interactive input,",
+ "not if you provide the selections, e.g., from a pipe.[PAR]",
+
+ "It is possible to use variables to store selection expressions.",
+ "A variable is defined with the following syntax:[BR]",
+ "[TT]VARNAME = EXPR ;[tt][BR]",
+ "where [TT]EXPR[tt] is any valid selection expression.",
+ "After this, [TT]VARNAME[tt] can be used anywhere where [TT]EXPR[tt]",
+ "would be valid.[PAR]",
+
+ "Selections are composed of three main types of expressions, those that",
+ "define atoms ([TT]ATOM_EXPR[tt]s), those that define positions",
+ "([TT]POS_EXPR[tt]s), and those that evaluate to numeric values",
+ "([TT]NUM_EXPR[tt]s). Each selection should be a [TT]POS_EXPR[tt]",
+ "or a [TT]ATOM_EXPR[tt] (the latter is automatically converted to",
+ "positions). The basic rules are as follows:[BR]",
+ "1. An expression like [TT]NUM_EXPR1 < NUM_EXPR2[tt] evaluates to an",
+ "[TT]ATOM_EXPR[tt] that selects all the atoms for which the comparison",
+ "is true.[BR]",
+ "2. Atom expressions can be combined with gmx_boolean operations such as",
+ "[TT]not ATOM_EXPR[tt], [TT]ATOM_EXPR and ATOM_EXPR[tt], or",
+ "[TT]ATOM_EXPR or ATOM_EXPR[tt]. Parentheses can be used to alter the",
+ "evaluation order.[BR]",
+ "3. [TT]ATOM_EXPR[tt] expressions can be converted into [TT]POS_EXPR[tt]",
+ "expressions in various ways, see \"help positions\" for more details.[PAR]",
+
+ "Some keywords select atoms based on string values such as the atom name.",
+ "For these keywords, it is possible to use wildcards ([TT]name \"C*\"[tt])",
+ "or regular expressions (e.g., [TT]resname \"R[AB]\"[tt]).",
+ "The match type is automatically guessed from the string: if it contains",
+ "other characters than letters, numbers, '*', or '?', it is interpreted",
+ "as a regular expression.",
+ "Strings that contain non-alphanumeric characters should be enclosed in",
+ "double quotes as in the examples. For other strings, the quotes are",
+ "optional, but if the value conflicts with a reserved keyword, a syntax",
+ "error will occur. If your strings contain uppercase letters, this should",
+ "not happen.[PAR]",
+
+ "Index groups provided with the [TT]-n[tt] command-line option or",
+ "generated by default can be accessed with [TT]group NR[tt] or",
+ "[TT]group NAME[tt], where [TT]NR[tt] is a zero-based index of the group",
+ "and [TT]NAME[tt] is part of the name of the desired group.",
+ "The keyword [TT]group[tt] is optional if the whole selection is",
+ "provided from an index group.",
+ "To see a list of available groups in the interactive mode, press enter",
+ "in the beginning of a line.",
+};
+
+static const t_selection_help_item helpitems[] = {
+ {NULL, asize(help_common), help_common},
+ {"cmdline", asize(help_cmdline), help_cmdline},
+ {"syntax", asize(help_syntax), help_syntax},
+ {"positions", asize(help_positions), help_positions},
+ {"arithmetic", asize(help_arithmetic), help_arithmetic},
+ {"keywords", asize(help_keywords), help_keywords},
+ {"evaluation", asize(help_eval), help_eval},
+ {"limitations", asize(help_limits), help_limits},
+ {"examples", asize(help_examples), help_examples},
+};
+
+/*! \brief
+ * Prints a brief list of keywords (selection methods) available.
+ *
+ * \param[in] symtab Symbol table to use to find available keywords.
+ * \param[in] type Only methods that return this type are printed.
+ * \param[in] bMod If FALSE, \ref SMETH_MODIFIER methods are excluded, otherwise
+ * only them are printed.
+ */
+static void
+print_keyword_list(gmx_sel_symtab_t *symtab, e_selvalue_t type,
+ gmx_bool bMod)
+{
+ gmx_sel_symrec_t *symbol;
+
+ symbol = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD);
+ while (symbol)
+ {
+ gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol);
+ gmx_bool bShow;
+ bShow = (method->type == type)
+ && ((bMod && (method->flags & SMETH_MODIFIER))
+ || (!bMod && !(method->flags & SMETH_MODIFIER)));
+ if (bShow)
+ {
+ fprintf(stderr, " %c ",
+ (method->help.nlhelp > 0 && method->help.help) ? '*' : ' ');
+ if (method->help.syntax)
+ {
+ fprintf(stderr, "%s\n", method->help.syntax);
+ }
+ else
+ {
+ const char *symname = _gmx_sel_sym_name(symbol);
+
+ fprintf(stderr, "%s", symname);
+ if (strcmp(symname, method->name) != 0)
+ {
+ fprintf(stderr, " (synonym for %s)", method->name);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD);
+ }
+}
+
+/*!
+ * \param[in] symtab Symbol table to use to find available keywords.
+ * \param[in] topic Topic to print help on, or NULL for general help.
+ *
+ * \p sc is used to get information on which keywords are available in the
+ * present context.
+ */
+void
+_gmx_sel_print_help(gmx_sel_symtab_t *symtab, const char *topic)
+{
+ const t_selection_help_item *item = NULL;
+ size_t i;
+
+ /* Find the item for the topic */
+ if (!topic)
+ {
+ item = &helpitems[0];
+ }
+ else if (strcmp(topic, "all") == 0)
+ {
+ for (i = 0; i < asize(helpitems); ++i)
+ {
+ item = &helpitems[i];
+ _gmx_sel_print_help(symtab, item->topic);
+ if (i != asize(helpitems) - 1)
+ {
+ fprintf(stderr, "\n\n");
+ }
+ }
+ return;
+ }
+ else
+ {
+ for (i = 1; i < asize(helpitems); ++i)
+ {
+ if (strncmp(helpitems[i].topic, topic, strlen(topic)) == 0)
+ {
+ item = &helpitems[i];
+ break;
+ }
+ }
+ }
+ /* If the topic is not found, check the available methods.
+ * If they don't provide any help either, tell the user and exit. */
+ if (!item)
+ {
+ gmx_sel_symrec_t *symbol;
+
+ symbol = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD);
+ while (symbol)
+ {
+ gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol);
+ if (method->help.nlhelp > 0 && method->help.help
+ && strncmp(method->name, topic, strlen(topic)) == 0)
+ {
+ print_tty_formatted(stderr, method->help.nlhelp,
+ method->help.help, 0, NULL, NULL, FALSE);
+ return;
+ }
+ symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD);
+ }
+
+ fprintf(stderr, "No help available for '%s'.\n", topic);
+ return;
+ }
+ /* Print the help */
+ print_tty_formatted(stderr, item->nl, item->text, 0, NULL, NULL, FALSE);
+ /* Special handling of certain pages */
+ if (!topic)
+ {
+ int len = 0;
+
+ /* Print the subtopics on the main page */
+ fprintf(stderr, "\nAvailable subtopics:\n");
+ for (i = 1; i < asize(helpitems); ++i)
+ {
+ int len1 = strlen(helpitems[i].topic) + 2;
+
+ len += len1;
+ if (len > 79)
+ {
+ fprintf(stderr, "\n");
+ len = len1;
+ }
+ fprintf(stderr, " %s", helpitems[i].topic);
+ }
+ fprintf(stderr, "\n");
+ }
+ else if (strcmp(item->topic, "keywords") == 0)
+ {
+ /* Print the list of keywords */
+ fprintf(stderr, "\nKeywords that select atoms by an integer property:\n");
+ fprintf(stderr, "(use in expressions or like \"atomnr 1 to 5 7 9\")\n");
+ print_keyword_list(symtab, INT_VALUE, FALSE);
+
+ fprintf(stderr, "\nKeywords that select atoms by a numeric property:\n");
+ fprintf(stderr, "(use in expressions or like \"occupancy 0.5 to 1\")\n");
+ print_keyword_list(symtab, REAL_VALUE, FALSE);
+
+ fprintf(stderr, "\nKeywords that select atoms by a string property:\n");
+ fprintf(stderr, "(use like \"name PATTERN [PATTERN] ...\")\n");
+ print_keyword_list(symtab, STR_VALUE, FALSE);
+
+ fprintf(stderr, "\nAdditional keywords that directly select atoms:\n");
+ print_keyword_list(symtab, GROUP_VALUE, FALSE);
+
+ fprintf(stderr, "\nKeywords that directly evaluate to positions:\n");
+ fprintf(stderr, "(see also \"help positions\")\n");
+ print_keyword_list(symtab, POS_VALUE, FALSE);
+
+ fprintf(stderr, "\nAdditional keywords:\n");
+ print_keyword_list(symtab, POS_VALUE, TRUE);
+ print_keyword_list(symtab, NO_VALUE, TRUE);
+ }
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Functions for printing help for selections.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_HELP_H
+#define GMX_SELECTION_HELP_H
+
+struct gmx_sel_symtab_t;
+
+/** Prints help for writing selections. */
+void
+_gmx_sel_print_help(struct gmx_sel_symtab_t *symtab, const char *topic);
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in selmethod.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <macros.h>
+#include <string2.h>
+
+#include "gromacs/selection/selmethod.h"
+
+#include "symrec.h"
+
+/*
+ * These global variables cannot be const because gmx_ana_selmethod_register()
+ * modifies them to set some defaults. This is a small price to pay for the
+ * convenience of not having to remember exactly how the selection compiler
+ * expects the structures to be filled, and even more so if the expectations
+ * change. Also, even if the gmx_ana_selmethod_t structures were made const,
+ * the parameters could not be without typecasts somewhere, because the param
+ * field in gmx_ana_selmethod_t cannot be declared const.
+ *
+ * Even though the variables may be modified, this should be thread-safe as
+ * modifications are done only in gmx_ana_selmethod_register(), and it should
+ * work even if called more than once for the same structure, and even if
+ * called concurrently from multiple threads (as long as the selection
+ * collection is not the same).
+ *
+ * All of these problems should go away if/when the selection methods are
+ * implemented as C++ classes.
+ */
+
+/* From sm_com.c */
+extern gmx_ana_selmethod_t sm_cog;
+extern gmx_ana_selmethod_t sm_com;
+/* From sm_simple.c */
+extern gmx_ana_selmethod_t sm_all;
+extern gmx_ana_selmethod_t sm_none;
+extern gmx_ana_selmethod_t sm_atomnr;
+extern gmx_ana_selmethod_t sm_resnr;
+extern gmx_ana_selmethod_t sm_resindex;
+extern gmx_ana_selmethod_t sm_molindex;
+extern gmx_ana_selmethod_t sm_atomname;
+extern gmx_ana_selmethod_t sm_atomtype;
+extern gmx_ana_selmethod_t sm_resname;
+extern gmx_ana_selmethod_t sm_insertcode;
+extern gmx_ana_selmethod_t sm_chain;
+extern gmx_ana_selmethod_t sm_mass;
+extern gmx_ana_selmethod_t sm_charge;
+extern gmx_ana_selmethod_t sm_altloc;
+extern gmx_ana_selmethod_t sm_occupancy;
+extern gmx_ana_selmethod_t sm_betafactor;
+extern gmx_ana_selmethod_t sm_x;
+extern gmx_ana_selmethod_t sm_y;
+extern gmx_ana_selmethod_t sm_z;
+/* From sm_distance.c */
+extern gmx_ana_selmethod_t sm_distance;
+extern gmx_ana_selmethod_t sm_mindistance;
+extern gmx_ana_selmethod_t sm_within;
+/* From sm_insolidangle.c */
+extern gmx_ana_selmethod_t sm_insolidangle;
+/* From sm_same.c */
+extern gmx_ana_selmethod_t sm_same;
+
+/* From sm_merge.c */
+extern gmx_ana_selmethod_t sm_merge;
+extern gmx_ana_selmethod_t sm_plus;
+/* From sm_permute.c */
+extern gmx_ana_selmethod_t sm_permute;
+
+/*! \internal \brief
+ * Helper structure for defining selection methods.
+ */
+typedef struct {
+ /*! \brief
+ * Name to register the method under.
+ *
+ * If NULL, use the actual name of the method.
+ * This field is used for defining synonyms.
+ */
+ const char *name;
+ /** Method data structure to register. */
+ gmx_ana_selmethod_t *method;
+} t_register_method;
+
+/** Array of selection methods defined in the library. */
+static const t_register_method smtable_def[] = {
+ {NULL, &sm_cog},
+ {NULL, &sm_com},
+
+ {NULL, &sm_all},
+ {NULL, &sm_none},
+ {NULL, &sm_atomnr},
+ {NULL, &sm_resnr},
+ {"resid", &sm_resnr},
+ {NULL, &sm_resindex},
+ {"residue", &sm_resindex},
+ {NULL, &sm_molindex},
+ {"mol", &sm_molindex},
+ {"molecule", &sm_molindex},
+ {NULL, &sm_atomname},
+ {NULL, &sm_atomtype},
+ {NULL, &sm_resname},
+ {NULL, &sm_insertcode},
+ {NULL, &sm_chain},
+ {NULL, &sm_mass},
+ {NULL, &sm_charge},
+ {NULL, &sm_altloc},
+ {NULL, &sm_occupancy},
+ {NULL, &sm_betafactor},
+ {NULL, &sm_x},
+ {NULL, &sm_y},
+ {NULL, &sm_z},
+
+ {NULL, &sm_distance},
+ {NULL, &sm_mindistance},
+ {NULL, &sm_within},
+ {NULL, &sm_insolidangle},
+ {NULL, &sm_same},
+
+ {NULL, &sm_merge},
+ {NULL, &sm_plus},
+ {NULL, &sm_permute},
+};
+
+/*! \brief
+ * Convenience function for reporting errors found in selection methods.
+ */
+static void
+report_error(FILE *fp, const char *name, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (fp)
+ {
+ fprintf(fp, "selection method '%s': ", name);
+ vfprintf(fp, fmt, ap);
+ fprintf(fp, "\n");
+ }
+ va_end(ap);
+}
+
+/*! \brief
+ * Convenience function for reporting errors found in selection method parameters.
+ */
+static void
+report_param_error(FILE *fp, const char *mname, const char *pname,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (fp)
+ {
+ fprintf(fp, "selection method '%s': parameter '%s': ", mname, pname);
+ vfprintf(fp, fmt, ap);
+ fprintf(fp, "\n");
+ }
+ va_end(ap);
+}
+
+/*! \brief
+ * Checks the validity of parameters.
+ *
+ * \param[in] fp File handle to use for diagnostic messages
+ * (can be NULL).
+ * \param[in] name Name of the method (used for error messages).
+ * \param[in] nparams Number of parameters in \p param.
+ * \param[in,out] param Parameter array
+ * (only the \c flags field of gmx_boolean parameters may be modified).
+ * \param[in] symtab Symbol table (used for checking overlaps).
+ * \returns TRUE if there are no problems with the parameters,
+ * FALSE otherwise.
+ *
+ * This function performs some checks common to both check_method() and
+ * check_modifier().
+ * The purpose of these checks is to ensure that the selection parser does not
+ * need to check for the validity of the parameters at each turn, and to
+ * report programming errors as early as possible.
+ * If you remove a check, make sure that the parameter parser can handle the
+ * resulting parameters.
+ */
+static gmx_bool
+check_params(FILE *fp, const char *name, int nparams, gmx_ana_selparam_t param[],
+ gmx_sel_symtab_t *symtab)
+{
+ gmx_bool bOk = TRUE;
+ gmx_sel_symrec_t *sym;
+ int i, j;
+
+ if (nparams > 0 && !param)
+ {
+ report_error(fp, name, "error: missing parameter data");
+ bOk = FALSE;
+ return FALSE;
+ }
+ if (nparams == 0 && param)
+ {
+ report_error(fp, name, "warning: parameter data unused because nparams=0");
+ }
+ /* Check each parameter */
+ for (i = 0; i < nparams; ++i)
+ {
+ /* Check that there is at most one NULL name, in the beginning */
+ if (param[i].name == NULL && i > 0)
+ {
+ report_error(fp, name, "error: NULL parameter should be the first one");
+ bOk = FALSE;
+ continue;
+ }
+ /* Check for duplicates */
+ for (j = 0; j < i; ++j)
+ {
+ if (param[j].name == NULL)
+ {
+ continue;
+ }
+ if (!gmx_strcasecmp(param[i].name, param[j].name))
+ {
+ report_error(fp, name, "error: duplicate parameter name '%s'", param[i].name);
+ bOk = FALSE;
+ break;
+ }
+ }
+ /* Check flags */
+ if (param[i].flags & SPAR_SET)
+ {
+ report_param_error(fp, name, param[i].name, "warning: flag SPAR_SET is set");
+ param[i].flags &= ~SPAR_SET;
+ }
+ if (param[i].flags & SPAR_RANGES)
+ {
+ if (param[i].val.type != INT_VALUE && param[i].val.type != REAL_VALUE)
+ {
+ report_param_error(fp, name, param[i].name, "error: SPAR_RANGES cannot be set for a non-numeric parameter");
+ bOk = FALSE;
+ }
+ if (param[i].flags & SPAR_DYNAMIC)
+ {
+ report_param_error(fp, name, param[i].name, "warning: SPAR_DYNAMIC does not have effect with SPAR_RANGES");
+ param[i].flags &= ~SPAR_DYNAMIC;
+ }
+ if (!(param[i].flags & SPAR_VARNUM) && param[i].val.nr != 1)
+ {
+ report_param_error(fp, name, param[i].name, "error: range should take either one or an arbitrary number of values");
+ bOk = FALSE;
+ }
+ if (param[i].flags & SPAR_ATOMVAL)
+ {
+ report_param_error(fp, name, param[i].name, "error: SPAR_RANGES and SPAR_ATOMVAL both set");
+ bOk = FALSE;
+ }
+ }
+ if ((param[i].flags & SPAR_VARNUM) && (param[i].flags & SPAR_ATOMVAL))
+ {
+ report_param_error(fp, name, param[i].name, "error: SPAR_VARNUM and SPAR_ATOMVAL both set");
+ bOk = FALSE;
+ }
+ if (param[i].flags & SPAR_ENUMVAL)
+ {
+ if (param[i].val.type != STR_VALUE)
+ {
+ report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL can only be set for string parameters");
+ bOk = FALSE;
+ }
+ if (param[i].val.nr != 1)
+ {
+ report_param_error(fp, name, param[i].name, "error: SPAR_ENUMVAL parameters should take exactly one value");
+ bOk = FALSE;
+ }
+ if (param[i].flags & (SPAR_DYNAMIC | SPAR_VARNUM | SPAR_ATOMVAL))
+ {
+ report_param_error(fp, name, param[i].name, "error: only SPAR_OPTIONAL supported with SPAR_ENUMVAL");
+ bOk = FALSE;
+ }
+ }
+ /* Check gmx_boolean parameters */
+ if (param[i].val.type == NO_VALUE)
+ {
+ if (param[i].val.nr != 0)
+ {
+ report_param_error(fp, name, param[i].name, "error: number of values should be zero for gmx_boolean parameters");
+ bOk = FALSE;
+ }
+ /* The gmx_boolean parameters should always be optional, so set the
+ * flag for convenience. */
+ param[i].flags |= SPAR_OPTIONAL;
+ /* Any other flags should not be specified */
+ if (param[i].flags & ~SPAR_OPTIONAL)
+ {
+ report_param_error(fp, name, param[i].name, "error: gmx_boolean parameter should not have any flags set");
+ bOk = FALSE;
+ }
+ }
+ /* Check val.nr */
+ if (param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL))
+ {
+ if (param[i].val.nr != -1)
+ {
+ report_param_error(fp, name, param[i].name, "warning: val.nr is not -1 although SPAR_VARNUM/SPAR_ATOMVAL is set");
+ }
+ param[i].val.nr = -1;
+ }
+ else if (param[i].val.type != NO_VALUE)
+ {
+ if (param[i].val.nr <= 0)
+ {
+ report_param_error(fp, name, param[i].name, "error: val.nr <= 0");
+ bOk = FALSE;
+ }
+ }
+ /* Check that the value pointer is NULL */
+ if (param[i].nvalptr != NULL)
+ {
+ report_param_error(fp, name, param[i].name, "warning: nvalptr is set");
+ }
+ if (param[i].val.u.ptr != NULL && !(param[i].flags & SPAR_ENUMVAL))
+ {
+ report_param_error(fp, name, param[i].name, "warning: value pointer is set");
+ }
+ /* Check that the name contains only valid characters */
+ if (param[i].name == NULL)
+ {
+ continue;
+ }
+ if (!isalpha(param[i].name[0]))
+ {
+ report_param_error(fp, name, param[i].name, "error: name does not begin with a letter");
+ bOk = FALSE;
+ continue;
+ }
+ for (j = 1; param[i].name[j] != 0; ++j)
+ {
+ if (param[i].name[j] != '_' && !isalnum(param[i].name[j]))
+ {
+ report_param_error(fp, name, param[i].name, "error: name contains non-alphanumeric characters");
+ bOk = FALSE;
+ break;
+ }
+ }
+ if (param[i].name[j] != 0)
+ {
+ continue;
+ }
+ /* Check that the name does not conflict with a method */
+ if (_gmx_sel_find_symbol(symtab, param[i].name, TRUE))
+ {
+ report_param_error(fp, name, param[i].name, "error: name conflicts with another method or a keyword");
+ bOk = FALSE;
+ }
+ } /* End of parameter loop */
+ /* Check parameters of existing methods */
+ sym = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD);
+ while (sym)
+ {
+ gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(sym);
+ gmx_ana_selparam_t *param =
+ gmx_ana_selmethod_find_param(name, method);
+ if (param)
+ {
+ report_param_error(fp, method->name, param->name, "error: name conflicts with another method or a keyword");
+ bOk = FALSE;
+ }
+ sym = _gmx_sel_next_symbol(sym, SYMBOL_METHOD);
+ }
+ return bOk;
+}
+
+/*! \brief
+ * Checks the validity of selection method callback functions.
+ *
+ * \param[in] fp File handle to use for diagnostic messages
+ * (can be NULL).
+ * \param[in] method The method to check.
+ * \returns TRUE if there are no problems, FALSE otherwise.
+ *
+ * This function performs some checks common to both check_method() and
+ * check_modifier().
+ * This function checks that all the required callbacks are defined, i.e.,
+ * not NULL, to find programming errors.
+ */
+static gmx_bool
+check_callbacks(FILE *fp, gmx_ana_selmethod_t *method)
+{
+ gmx_bool bOk = TRUE;
+ gmx_bool bNeedInit;
+ int i;
+
+ /* Make some checks on init_data and free */
+ if (method->nparams > 0 && !method->init_data)
+ {
+ report_error(fp, method->name, "error: init_data should be provided because the method has parameters");
+ bOk = FALSE;
+ }
+ if (method->free && !method->init_data)
+ {
+ report_error(fp, method->name, "warning: free is not used because of missing init_data");
+ }
+ /* Check presence of outinit for position-valued methods */
+ if (method->type == POS_VALUE && !method->outinit)
+ {
+ report_error(fp, method->name, "error: outinit should be provided because the method has POS_VALUE");
+ bOk = FALSE;
+ }
+ /* Warn of dynamic callbacks in static methods */
+ if (!(method->flags & SMETH_MODIFIER))
+ {
+ if (method->pupdate && !(method->flags & SMETH_DYNAMIC))
+ {
+ report_error(fp, method->name, "warning: pupdate not used because the method is static");
+ method->pupdate = NULL;
+ }
+ }
+ /* Check that there is an evaluation function */
+ if (method->type != NO_VALUE && !method->update && !method->pupdate)
+ {
+ report_error(fp, method->name, "error: evaluation function missing");
+ bOk = FALSE;
+ }
+ /* Loop through the parameters to determine if initialization callbacks
+ * are needed. */
+ bNeedInit = FALSE;
+ for (i = 0; i < method->nparams; ++i)
+ {
+ if (method->param[i].val.type != POS_VALUE
+ && (method->param[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
+ {
+ bNeedInit = TRUE;
+ }
+ }
+ /* Check that the callbacks required by the parameters are present */
+ if (bNeedInit && !method->init)
+ {
+ report_error(fp, method->name, "error: init should be provided");
+ bOk = FALSE;
+ }
+ return bOk;
+}
+
+/*!
+ * Checks the validity of a selection method.
+ *
+ * \param[in] fp File handle to use for diagnostic messages
+ * (can be NULL).
+ * \param[in,out] method Method to check.
+ * \param[in] symtab Symbol table (used for checking overlaps).
+ *
+ * Checks the validity of the given selection method data structure
+ * that does not have \ref SMETH_MODIFIER set.
+ * If you remove a check, please make sure that the selection parser,
+ * compiler, and evaluation functions can deal with the method.
+ */
+static gmx_bool
+check_method(FILE *fp, gmx_ana_selmethod_t *method, gmx_sel_symtab_t *symtab)
+{
+ gmx_bool bOk = TRUE;
+
+ /* Check the type */
+ if (method->type == NO_VALUE)
+ {
+ report_error(fp, method->name, "error: no value type specified");
+ bOk = FALSE;
+ }
+ if (method->type == STR_VALUE && method->nparams > 0)
+ {
+ report_error(fp, method->name, "error: evaluates to a string but is not a keyword");
+ bOk = FALSE;
+ }
+ /* Check flags */
+ if (method->type == GROUP_VALUE)
+ {
+ /* Group methods should always have SMETH_SINGLEVAL,
+ * so set it for convenience. */
+ method->flags |= SMETH_SINGLEVAL;
+ /* Check that conflicting flags are not present. */
+ if (method->flags & SMETH_VARNUMVAL)
+ {
+ report_error(fp, method->name, "error: SMETH_VARNUMVAL cannot be set for group-valued methods");
+ bOk = FALSE;
+ }
+ }
+ else
+ {
+ if ((method->flags & SMETH_SINGLEVAL)
+ && (method->flags & SMETH_VARNUMVAL))
+ {
+ report_error(fp, method->name, "error: SMETH_SINGLEVAL and SMETH_VARNUMVAL both set");
+ bOk = FALSE;
+ }
+ }
+ if ((method->flags & SMETH_CHARVAL) && method->type != STR_VALUE)
+ {
+ report_error(fp, method->name, "error: SMETH_CHARVAL can only be specified for STR_VALUE methods");
+ bOk = FALSE;
+ }
+ /* Check the parameters */
+ if (!check_params(fp, method->name, method->nparams, method->param, symtab))
+ {
+ bOk = FALSE;
+ }
+ /* Check the callback pointers */
+ if (!check_callbacks(fp, method))
+ {
+ bOk = FALSE;
+ }
+
+ return bOk;
+}
+
+/*!
+ * Checks the validity of a selection modifier method.
+ *
+ * \param[in] fp File handle to use for diagnostic messages
+ * (can be NULL).
+ * \param[in,out] method Method to check.
+ * \param[in] symtab Symbol table (used for checking overlaps).
+ *
+ * Checks the validity of the given selection method data structure
+ * that has \ref SMETH_MODIFIER set.
+ * If you remove a check, please make sure that the selection parser,
+ * compiler, and evaluation functions can deal with the method.
+ */
+static gmx_bool
+check_modifier(FILE *fp, gmx_ana_selmethod_t *method, gmx_sel_symtab_t *symtab)
+{
+ gmx_bool bOk = TRUE;
+
+ /* Check the type */
+ if (method->type != NO_VALUE && method->type != POS_VALUE)
+ {
+ report_error(fp, method->name, "error: modifier should have type POS_VALUE or NO_VALUE");
+ bOk = FALSE;
+ }
+ /* Check flags */
+ if (method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
+ {
+ report_error(fp, method->name, "error: modifier should not have SMETH_SINGLEVAL or SMETH_VARNUMVAL set");
+ bOk = FALSE;
+ }
+ /* Check the parameters */
+ /* The first parameter is skipped */
+ if (!check_params(fp, method->name, method->nparams-1, method->param+1, symtab))
+ {
+ bOk = FALSE;
+ }
+ /* Check the callback pointers */
+ if (!check_callbacks(fp, method))
+ {
+ bOk = FALSE;
+ }
+ if (method->update)
+ {
+ report_error(fp, method->name, "error: modifier should not have update");
+ bOk = FALSE;
+ }
+ if (method->type == POS_VALUE && !method->pupdate)
+ {
+ report_error(fp, method->name, "error: evaluation function missing");
+ bOk = FALSE;
+ }
+
+ return bOk;
+}
+
+/*!
+ * \param[in,out] symtab Symbol table to register the method to.
+ * \param[in] name Name under which the method should be registered.
+ * \param[in] method Method to register.
+ * \returns 0 on success, EINVAL if there was something wrong with the
+ * method.
+ *
+ * \p name does not need to match the name of the method, and the same
+ * method can be registered multiple times under different names.
+ * If \p name equals some previously registered name,
+ * an error message is printed and the method is not registered.
+ *
+ * The function also performs some sanity checking on the input method,
+ * and refuses to register it if there are problems.
+ * Some problems only generate warnings.
+ * All problems are described to \p stderr.
+ */
+int
+gmx_ana_selmethod_register(gmx_sel_symtab_t *symtab,
+ const char *name, gmx_ana_selmethod_t *method)
+{
+ gmx_bool bOk;
+
+ /* Check the method */
+ if (method->flags & SMETH_MODIFIER)
+ {
+ bOk = check_modifier(stderr, method, symtab);
+ }
+ else
+ {
+ bOk = check_method(stderr, method, symtab);
+ }
+ /* Try to register the method if everything is ok */
+ if (bOk)
+ {
+ if (!_gmx_sel_add_method_symbol(symtab, name, method))
+ {
+ bOk = FALSE;
+ }
+ }
+ if (!bOk)
+ {
+ report_error(stderr, name, "warning: not registered");
+ return EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in,out] symtab Symbol table to register the methods to.
+ * \returns 0 on success, -1 if any of the default methods could not be
+ * registered.
+ */
+int
+gmx_ana_selmethod_register_defaults(gmx_sel_symtab_t *symtab)
+{
+ size_t i;
+ int rc;
+ gmx_bool bOk;
+
+ bOk = TRUE;
+ for (i = 0; i < asize(smtable_def); ++i)
+ {
+ gmx_ana_selmethod_t *method = smtable_def[i].method;
+
+ if (smtable_def[i].name == NULL)
+ {
+ rc = gmx_ana_selmethod_register(symtab, method->name, method);
+ }
+ else
+ {
+ rc = gmx_ana_selmethod_register(symtab, smtable_def[i].name, method);
+ }
+ if (rc != 0)
+ {
+ bOk = FALSE;
+ }
+ }
+ return bOk ? 0 : -1;
+}
+
+/*!
+ * \param[in] name Name of the parameter to search.
+ * \param[in] method Method to search for the parameter.
+ * \returns Pointer to the parameter in the
+ * \ref gmx_ana_selmethod_t::param "method->param" array,
+ * or NULL if no parameter with name \p name was found.
+ *
+ * This is a simple wrapper for gmx_ana_selparam_find().
+ */
+gmx_ana_selparam_t *
+gmx_ana_selmethod_find_param(const char *name, gmx_ana_selmethod_t *method)
+{
+ return gmx_ana_selparam_find(name, method->nparams, method->param);
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \page page_module_selection_custom Custom selection methods
+ *
+ * Custom selection methods are defined by creating a new instance of
+ * \c gmx_ana_selmethod_t and filling it with the necessary data for handling
+ * the selection.
+ * The structure contains callback pointers that define the actual behavior
+ * of the method.
+ * The following sections discuss how the structure should be filled and how
+ * to implement the callbacks.
+ *
+ *
+ * \section selmethods_define \c gmx_ana_selmethod_t data structure
+ *
+ * An example \c gmx_ana_selmethod_t definition could look like this:
+ *
+ * \code
+ * gmx_ana_selmethod_t sm_example = {
+ * "example", GROUP_VALUE, 0,
+ * asize(sm_params_example), sm_params_example,
+ * &init_data_example,
+ * NULL,
+ * &init_example,
+ * NULL,
+ * &free_data_example,
+ * &init_frame_example,
+ * &evaluate_example,
+ * NULL,
+ * {"example from POS_EXPR [cutoff REAL]", 0, NULL},
+ * };
+ * \endcode
+ *
+ * The first value defines the name of the method.
+ * It is used mostly for informational purposes; the actual name(s) recognized
+ * by the selection parser are defined by the call to
+ * gmx_ana_selmethod_register() (see \ref selmethods_register).
+ *
+ * The second value defines the type of the value the method returns.
+ * Possible values are
+ * - \ref NO_VALUE : This is allowed only for methods that have the flag
+ * \ref SMETH_MODIFIER set (see \ref selmethods_modifiers).
+ * - \ref INT_VALUE : The method returns one or more integer values.
+ * - \ref REAL_VALUE : The method returns one or more floating-point values.
+ * - \ref STR_VALUE : The method returns one or more strings.
+ * - \ref POS_VALUE : The method returns one or more 3D vectors.
+ * - \ref GROUP_VALUE : The method returns a single index group.
+ *
+ * The third value gives additional information about the method using
+ * a combination of flags.
+ * Possible flags are:
+ * - \ref SMETH_REQTOP : If set, the topology information is always loaded
+ * and the \p top pointer passed to the callbacks is guaranteed to be
+ * non-NULL. Should be set if the method requires topology information
+ * for evaluation.
+ * - \ref SMETH_DYNAMIC : If set, the method can only be evaluated dynamically,
+ * i.e., it requires data from the trajectory frame.
+ * - \ref SMETH_MODIFIER : If set, the method is a selection modifier and
+ * not an actual selection method.
+ * For more details, see \ref selmethods_modifiers.
+ *
+ * There are two additional flags that specify the number of values the
+ * method returns. Only one of them can be set at a time.
+ * If neither is set, the default behavior is to evaluate a value for each
+ * input atom (except for \ref GROUP_VALUE methods, which always return a
+ * single group).
+ * Other behaviors can be specified with these flags:
+ * - \ref SMETH_SINGLEVAL : If set, the method evaluates to a single value.
+ * This is automatically set if the type is \ref GROUP_VALUE.
+ * - \ref SMETH_VARNUMVAL : If set, the method evaluates to an arbitrary
+ * number of values.
+ * The number of values is determined based on the values given by the user
+ * to the method parameters (see \ref selmethods_params).
+ * .
+ * If either of these flags is specified (and the method type is not
+ * \ref GROUP_VALUE), the group passed to the evaluation callback should not
+ * be used as it can be NULL.
+ * Currently, the above flags only work (have been tested) for \ref POS_VALUE
+ * methods.
+ *
+ * There is one additional flag that can only be specified for \ref STR_VALUE
+ * methods: \ref SMETH_CHARVAL . It is meant for to ease implementation of
+ * methods that evaluate to strings consisting of single characters.
+ *
+ * The next two values determine the number of parameters and a pointer to
+ * the parameter array. The contents of the parameter array are described in
+ * \ref selmethods_params. If the method does not take parameters, the first
+ * value should be 0 and the second can be NULL.
+ * Currently, \ref STR_VALUE methods cannot take parameters, but this limitation
+ * should be easy to lift if required.
+ *
+ * These are followed by function callbacks that determine the
+ * actual behavior of the method. Any of these except the evaluation callback
+ * can be NULL (the evaluation callback can also be NULL if \ref NO_VALUE is
+ * specified for a selection modifier). However, the presence of parameters
+ * can require some of the callbacks to be implemented.
+ * The details are described in \ref selmethods_callbacks.
+ *
+ * Finally, there is a data structure that gives help texts for the method.
+ *
+ * The \c gmx_ana_selmethod_t variable should be declared as a global variable
+ * or it should be otherwise ensured that the structure is not freed: only a
+ * pointer to the structure is stored by the library.
+ *
+ *
+ * \section selmethods_params Defining parameters
+ *
+ * Parameters to selection methods are defined in a separate array of
+ * \c gmx_ana_selparam_t structures.
+ * The order of the parameters does not matter (except possibly for callback
+ * implementation), with one important exception:
+ * If the method evaluates to a \ref POS_VALUE, the first parameter should
+ * have \ref GROUP_VALUE and be the one that is used to calculate the
+ * positions.
+ *
+ * An example parameter definition:
+ * \code
+ * static gmx_ana_selparam_t sm_params_example[] = {
+ * {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
+ * {"from", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+ * };
+ * \endcode
+ *
+ * The first value gives the name of the parameter.
+ * The first parameter can have a NULL name, which means that the value should
+ * immediately follow the method name. This can be used to specify methods
+ * of the type 'within 5 of ...'.
+ *
+ * The second value specifies the type of the value that the parameter accepts.
+ * \ref NO_VALUE can be used to specify a boolean parameter, other possibilities
+ * are the same as for the selection method type.
+ *
+ * The third value gives the number of values that the parameter accepts.
+ * For boolean parameters (\ref NO_VALUE), it should be 0.
+ * For parameters with \ref SPAR_VARNUM of \ref SPAR_ATOMVAL, it should be set
+ * to -1 for consistency (it is not used).
+ * If \ref SPAR_RANGES is specified, it should be either 1 (to accept a single
+ * continuous range) or -1 (if combined with \ref SPAR_VARNUM).
+ * In all other cases, it should be a positive integer; in most cases, it
+ * should be 1.
+ *
+ * The nest two pointers should always be NULL (they should be initialized in
+ * the callbacks), except the first pointer in the case of \ref SPAR_ENUMVAL
+ * (see below).
+ *
+ * The final value gives additional information about the acceptable values
+ * for the parameter using a combination of flags.
+ * The possible flags are:
+ * - \ref SPAR_OPTIONAL : If set, the user does not need to provide a value
+ * for the parameter. If not set, an error is reported if the parameter
+ * is not specified by the user.
+ * - \ref SPAR_DYNAMIC : If set, the method can handle dynamic values for
+ * the parameter, i.e., the value(s) can be given by an expression that
+ * evaluates to different values for different frames.
+ * - \ref SPAR_RANGES : Can be set only for \ref INT_VALUE and
+ * \ref REAL_VALUE parameters,
+ * and cannot be combined with \ref SPAR_DYNAMIC.
+ * If set, the parameter accepts ranges of values.
+ * The ranges are automatically sorted and compacted such that a minimum
+ * amount of non-overlapping ranges are given for the method.
+ * - \ref SPAR_VARNUM : If set, the parameter can have a variable number
+ * of values. These can be provided by the user as a list of values, or
+ * using a single \ref SMETH_VARNUMVAL (or a single \ref SMETH_SINGLEVAL)
+ * method.
+ * - \ref SPAR_ATOMVAL : If set, the parameter accepts either a single value
+ * or an expression that evaluates to a value for each input atom.
+ * The single input value is treated as if the same value was returned for
+ * each atom.
+ * Cannot be combined with \ref SPAR_RANGES or \ref SPAR_VARNUM.
+ * - \ref SPAR_ENUMVAL : Can only be set for \ref STR_VALUE parameters that
+ * take a single value, and cannot be combined with any other flag than
+ * \ref SPAR_OPTIONAL. If set, the parameter only accepts one of predefined
+ * string values. See \ref SPAR_ENUMVAL documentation for details on how
+ * to specify the acceptable values.
+ *
+ *
+ * \section selmethods_callbacks Implementing callbacks
+ *
+ * There are eight differen callback functions that can be implemented for
+ * selection methods: sel_datafunc(), sel_posfunc(), sel_initfunc(),
+ * sel_outinitfunc(), sel_freefunc(), sel_framefunc(), and two update functions.
+ * They are in this order in the \c gmx_ana_selmethod_t data structure.
+ * In general, any of the callbacks can be NULL, but the presence of
+ * parameters or other callbacks imposes some restrictions:
+ * - sel_datafunc() should be provided if the method takes parameters.
+ * - sel_initfunc() should be provided if the method takes
+ * any parameters with the \ref SPAR_VARNUM or \ref SPAR_ATOMVAL flags,
+ * except if those parameters have a \ref POS_VALUE.
+ * - sel_outinitfunc() should be provided for \ref POS_VALUE methods
+ * and \ref SMETH_VARNUMVAL methods.
+ * - sel_freefunc() should be provided if sel_datafunc() and/or
+ * sel_initfunc() allocate any dynamic memory in addition to the data
+ * structure itself.
+ * - sel_updatefunc_pos() only makes sense for methods with \ref SMETH_DYNAMIC
+ * set.
+ * - At least one update function should be provided unless the method type is
+ * \ref NO_VALUE.
+ *
+ * The documentations for the function pointer types provide more information
+ * about how the callbacks should be implemented.
+ *
+ *
+ * \section selmethods_modifiers Selection modifiers
+ *
+ * Selection modifiers are a special kind of selection methods that can be
+ * appended to the end of a selection. They are specified by adding the
+ * \ref SMETH_MODIFIER flag to the \c gmx_ana_selmethod_t.
+ * They can have two different types:
+ * - \ref POS_VALUE : These modifiers are given the final positions
+ * as an input, and they can make modifications to the selection that are
+ * not possible otherwise (e.g., permute the atoms).
+ * The modifier should implement sel_updatefunc_pos() and also have
+ * one NULL parameter in the beginning of the parameter list that takes
+ * \ref POS_VALUE and is used to give the input positions.
+ * - \ref NO_VALUE : These modifiers do not modify the final selection, but
+ * can be used to implement per-selection options for analysis tools
+ * or to control the default behavior of the selection engine
+ * (currently, such a framework is not implemented, but should be easy to
+ * implement if required).
+ *
+ * In addition to restricting the type of the method, selection modifiers
+ * do not allow the flags \ref SMETH_SINGLEVAL and \ref SMETH_VARNUMVAL
+ * (they would not make sense).
+ *
+ * Parameters and callbacks should be implemented as with normal selection
+ * method, but beware that very little of the functionality has been tested.
+ *
+ * \todo
+ * The modifier handling could be made more flexible and more generic;
+ * the current implementation does not allow many things which would be
+ * possible with slight changes in the internals of the library.
+ *
+ *
+ * \section selmethods_register Registering the method
+ *
+ * After defining the method with \c gmx_ana_selmethod_t, it should be
+ * registered with the selection engine.
+ * In analysis programs, this can be done by calling
+ * gmx_ana_selmethod_register().
+ * If adding the method to the library, you should add a pointer to the new
+ * method structure into the \c smtable_def array (in selmethod.cpp), and it is
+ * registered automatically.
+ * In both cases, gmx_ana_selmethod_register() does several checks on the
+ * structure and reports any errors or inconsistencies it finds.
+ */
+/*! \file
+ * \brief API for handling selection methods.
+ *
+ * There should be no need to use the data structures or call the
+ * functions in this file directly unless implementing a custom selection
+ * method.
+ *
+ * Instructions for implementing custom selection methods can be found
+ * on a separate page: \ref page_module_selection_custom
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELMETHOD_H
+#define GMX_SELECTION_SELMETHOD_H
+
+#include "../legacyheaders/typedefs.h"
+
+#include "indexutil.h"
+#include "selparam.h"
+#include "selvalue.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct gmx_ana_pos_t;
+struct gmx_ana_poscalc_coll_t;
+struct gmx_ana_selcollection_t;
+
+/*! \name Selection method flags
+ * \anchor selmethod_flags
+ */
+/*@{*/
+/*! \brief
+ * If set, the method requires topology information.
+ */
+#define SMETH_REQTOP 1
+/*! \brief
+ * If set, the method can only be evaluated dynamically.
+ */
+#define SMETH_DYNAMIC 2
+/*! \brief
+ * If set, the method evaluates to a single value.
+ *
+ * The default is that the method evaluates to a value for each input atom.
+ * Cannot be combined with \ref SMETH_VARNUMVAL.
+ */
+#define SMETH_SINGLEVAL 4
+/*! \brief
+ * If set, the method evaluates to an arbitrary number of values.
+ *
+ * The default is that the method evaluates to a value for each input atom.
+ * Cannot be combined with \ref SMETH_SINGLEVAL or with \ref GROUP_VALUE.
+ */
+#define SMETH_VARNUMVAL 8
+/*! \brief
+ * If set, the method evaluates to single-character strings.
+ *
+ * This flag can only be set for \ref STR_VALUE methods. If it is set, the
+ * selection engine automatically allocates and frees the required strings.
+ * The evaluation function should store the character values as the first
+ * character in the strings in the output data structure and should not change
+ * the string pointers.
+ */
+#define SMETH_CHARVAL 64
+/*! \brief
+ * If set, the method is a selection modifier.
+ *
+ * The method type should be \ref GROUP_VALUE or \ref NO_VALUE .
+ * Cannot be combined with \ref SMETH_SINGLEVAL or \ref SMETH_VARNUMVAL .
+ */
+#define SMETH_MODIFIER 256
+/*@}*/
+
+/*! \brief
+ * Allocates and initializes internal data and parameter values.
+ *
+ * \param[in] npar Number of parameters in \p param.
+ * \param[in,out] param Pointer to (a copy of) the method's
+ * \c gmx_ana_selmethod_t::param.
+ * \returns Pointer to method-specific data structure.
+ * This pointer will be passed as the last parameter of all other function
+ * calls.
+ * Should return NULL on error (only error that should occur is out of
+ * memory).
+ *
+ * Should allocate and initialize any internal data required by the method.
+ * Should also initialize the value pointers (\c gmx_ana_selparam_t::val) in
+ * \p param to point to variables within the internal data structure,
+ * with the exception of parameters that specify the \ref SPAR_VARNUM or
+ * the \ref SPAR_ATOMVAL flag (these should be handled in sel_initfunc()).
+ * However, parameters with a position value should be initialized.
+ * It is also possible to initialize \ref SPAR_ENUMVAL statically outside
+ * this function (see \ref SPAR_ENUMVAL).
+ * The \c gmx_ana_selparam_t::nvalptr should also be initialized for
+ * non-position-valued parameters that have both \ref SPAR_VARNUM and
+ * \ref SPAR_DYNAMIC set (it can also be initialized for other parameters if
+ * desired, but the same information will be available through other means).
+ * For optional parameters, the default values can (and should) be initialized
+ * here, as the parameter values are not changed if the parameter is not
+ * provided.
+ *
+ * For boolean parameters (type equals \ref NO_VALUE), the default value
+ * should be set here. The user can override the value by giving the parameter
+ * either as 'NAME'/'noNAME', or as 'NAME on/off/yes/no'.
+ *
+ * If the method takes any parameters, this function must be provided.
+ */
+typedef void *(*sel_datafunc)(int npar, gmx_ana_selparam_t *param);
+/*! \brief
+ * Sets the position calculation collection for the method.
+ *
+ * \param[in] pcc Position calculation collection that the method should use
+ * for position calculations.
+ * \param data Internal data structure from sel_datafunc().
+ *
+ * This function should be provided if the method uses the routines from
+ * poscalc.h for calculating positions.
+ * The pointer \p pcc should then be stored and used for initialization for
+ * any position calculation structures.
+ */
+typedef void (*sel_posfunc)(struct gmx_ana_poscalc_coll_t *pcc, void *data);
+/*! \brief
+ * Does initialization based on topology and/or parameter values.
+ *
+ * \param[in] top Topology structure
+ * (can be NULL if \ref SMETH_REQTOP is not set).
+ * \param[in] npar Number of parameters in \p param.
+ * \param[in] param Pointer to (an initialized copy of) the method's
+ * \c gmx_ana_selmethod_t::param.
+ * \param data Internal data structure from sel_datafunc().
+ * \returns 0 on success, a non-zero error code on failure.
+ *
+ * This function is called after the parameters have been processed:
+ * the values of the parameters are stored at the locations set in
+ * sel_datafunc().
+ * The flags \ref SPAR_DYNAMIC and \ref SPAR_ATOMVAL are cleared before
+ * calling the function if the value is static or single-valued, respectively.
+ * If a parameter had the \ref SPAR_VARNUM or \ref SPAR_ATOMVAL flag (and
+ * is not \ref POS_VALUE), a pointer to the memory allocated for the values is
+ * found in \c gmx_ana_selparam_t::val.
+ * The pointer should be stored by this function, otherwise the values
+ * cannot be accessed.
+ * For \ref SPAR_VARNUM parameters, the number of values can be accessed
+ * through \c gmx_ana_selparam_t::val. For parameters with \ref SPAR_DYNAMIC,
+ * the number is the maximum number of values (the actual number can be
+ * accessed in sel_framefunc() and in the update callback through the value
+ * pointed by \c gmx_ana_selparam_t::nvalptr).
+ * For \ref SPAR_ATOMVAL parameters, \c gmx_ana_selparam_t::val::nr is set to
+ * 1 if a single value was provided, otherwise it is set to the maximum number
+ * of values possibly passed to the method.
+ * The value pointed by \c gmx_ana_selparam_t::nvalptr always contains the same
+ * value as \c gmx_ana_selparam_t::val::nr.
+ *
+ * For dynamic \ref GROUP_VALUE parameters (\ref SPAR_DYNAMIC set), the value
+ * will be the largest possible selection that may occur during the
+ * evaluation. For other types of dynamic parameters, the values are
+ * undefined.
+ *
+ * If the method takes any parameters with the \ref SPAR_VARNUM or
+ * \ref SPAR_ATOMVAL flags, this function must be provided, except if these
+ * parameters all have \ref POS_VALUE.
+ *
+ * This function may be called multiple times for the same method if the
+ * method takes parameters with \ref SPAR_ATOMVAL set.
+ */
+typedef int (*sel_initfunc)(t_topology *top, int npar,
+ gmx_ana_selparam_t *param, void *data);
+/*! \brief
+ * Initializes output data structure.
+ *
+ * \param[in] top Topology structure
+ * (can be NULL if \ref SMETH_REQTOP is not set).
+ * \param[in,out] out Output data structure.
+ * \param[in] data Internal data structure from sel_datafunc().
+ * \returns 0 on success, an error code on error.
+ *
+ * This function is called immediately after sel_initfunc().
+ *
+ * If the method evaluates to a position (\ref POS_VALUE), this function
+ * should be provided, and it should initialize the \c gmx_ana_pos_t data
+ * structure pointed by \p out.p (the pointer is guaranteed to be non-NULL).
+ * The \p out.p->g pointer should be initialized to the group that is used
+ * to evaluate positions in sel_updatefunc() or sel_updatefunc_pos().
+ *
+ * The function should also be provided for non-position-valued
+ * \ref SMETH_VARNUMVAL methods. For these methods, it suffices to set the
+ * \p out->nr field to reflect the maximum number of values returned by the
+ * method.
+ *
+ * Currently, this function is not needed for other types of methods.
+ *
+ * This function may be called multiple times for the same method if the
+ * method takes parameters with \ref SPAR_ATOMVAL set.
+ */
+typedef int (*sel_outinitfunc)(t_topology *top, gmx_ana_selvalue_t *out,
+ void *data);
+/*! \brief
+ * Frees the internal data.
+ *
+ * \param[in] data Internal data structure from sel_datafunc().
+ *
+ * This function should be provided if the internal data structure contains
+ * dynamically allocated data, and should free any such data.
+ * The data structure itself should not be freed; this is handled automatically.
+ * If there is no dynamically allocated data within the structure,
+ * this function is not needed.
+ * Any memory pointers received as values of parameters are managed externally,
+ * and should not be freed.
+ * Pointers set as the value pointer of \ref SPAR_ENUMVAL parameters should not
+ * be freed.
+ */
+typedef void (*sel_freefunc)(void *data);
+
+/*! \brief
+ * Initializes the evaluation for a new frame.
+ *
+ * \param[in] top Topology structure
+ * (can be NULL if \ref SMETH_REQTOP is not set).
+ * \param[in] fr Current frame.
+ * \param[in] pbc Initialized periodic boundary condition structure,
+ * or NULL if PBC should not be used.
+ * \param data Internal data structure from sel_datafunc().
+ * \returns 0 on success, a non-zero error code on failure.
+ *
+ * This function should be implemented if the selection method needs to
+ * do some preprocessing for each frame, and the preprocessing does not
+ * depend on the evaluation group.
+ * Because \p sel_updatefunc_* can be called more than once for a frame,
+ * it is inefficient do the preprocessing there.
+ * It is ensured that this function will be called before
+ * \p sel_updatefunc_* for each frame, and that it will be called at most
+ * once for each frame.
+ * For static methods, it is called once, with \p fr and \p pbc set to
+ * NULL.
+ */
+typedef int (*sel_framefunc)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ void *data);
+/*! \brief
+ * Evaluates a selection method.
+ *
+ * \param[in] top Topology structure
+ * (can be NULL if \ref SMETH_REQTOP is not set).
+ * \param[in] fr Current frame.
+ * \param[in] pbc Initialized periodic boundary condition structure,
+ * or NULL if PBC should not be used.
+ * \param[in] g Index group for which the method should be evaluated.
+ * \param[out] out Output data structure.
+ * \param data Internal data structure from sel_datafunc().
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * This function should evaluate the method for each atom included in \p g,
+ * and write the output to \p out. The pointer in the union \p out->u that
+ * corresponds to the type of the method should be used.
+ * Enough memory has been allocated to store the output values.
+ * The number of values in \p out should also be updated if necessary.
+ * However, \ref POS_VALUE or \ref GROUP_VALUE methods should not touch
+ * \p out->nr (it should be 1 anyways).
+ *
+ * For \ref STR_VALUE methods, the pointers stored in \p out->s are discarded
+ * without freeing; it is the responsibility of this function to provide
+ * pointers that can be discarded without memory leaks.
+ */
+typedef int (*sel_updatefunc)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out,
+ void *data);
+/*! \brief
+ * Evaluates a selection method using positions.
+ *
+ * \param[in] top Topology structure
+ * (can be NULL if \ref SMETH_REQTOP is not set).
+ * \param[in] fr Current frame.
+ * \param[in] pbc Initialized periodic boundary condition structure,
+ * or NULL if PBC should not be used.
+ * \param[in] pos Positions for which the method should be evaluated.
+ * \param[out] out Output data structure.
+ * \param data Internal data structure from sel_datafunc().
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * This function should evaluate the method for each position in \p g,
+ * and write the output values to \p out. The pointer in the union \p out->u
+ * that corresponds to the type of the method should be used.
+ * Enough memory has been allocated to store the output values.
+ * The number of values in \p out should also be updated if necessary.
+ * However, \ref POS_VALUE or \ref GROUP_VALUE methods should not touch
+ * \p out->nr (it should be 1 anyways).
+ *
+ * For \ref STR_VALUE methods, the pointers stored in \p out->s are discarded
+ * without freeing; it is the responsibility of this function to provide
+ * pointers that can be discarded without memory leaks.
+ */
+typedef int (*sel_updatefunc_pos)(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ struct gmx_ana_pos_t *pos,
+ gmx_ana_selvalue_t *out,
+ void *data);
+
+/*! \brief
+ * Help information for a selection method.
+ *
+ * If some information is not available, the corresponding field can be set to
+ * 0/NULL.
+ */
+typedef struct gmx_ana_selmethod_help_t
+{
+ /*! \brief
+ * One-line description of the syntax of the method.
+ *
+ * If NULL, the name of the method is used.
+ */
+ const char *syntax;
+ /*! \brief
+ * Number of strings in \p help.
+ *
+ * Set to 0 if \p help is NULL.
+ */
+ int nlhelp;
+ /*! \brief
+ * Detailed help for the method.
+ *
+ * If there is no help available in addition to \p syntax, this can be set
+ * to NULL.
+ */
+ const char **help;
+} gmx_ana_selmethod_help_t;
+
+/*! \brief
+ * Describes a selection method.
+ *
+ * Any of the function pointers except the update call can be NULL if the
+ * operation is not required or not supported. In this case,
+ * corresponding function calls are skipped.
+ *
+ * See the function pointer type documentation for details of how the
+ * functions should be implemented.
+ * More details on implementing new selection methods can be found on a
+ * separate page: \ref page_module_selection_custom.
+ */
+typedef struct gmx_ana_selmethod_t
+{
+ /** Name of the method. */
+ const char *name;
+ /** Type which the method returns. */
+ e_selvalue_t type;
+ /*! \brief
+ * Flags to specify how the method should be handled.
+ *
+ * See \ref selmethod_flags for allowed values.
+ */
+ int flags;
+ /** Number of parameters the method takes. */
+ int nparams;
+ /** Pointer to the array of parameter descriptions. */
+ gmx_ana_selparam_t *param;
+
+ /** Function for allocating and initializing internal data and parameters. */
+ sel_datafunc init_data;
+ /** Function to set the position calculation collection. */
+ sel_posfunc set_poscoll;
+ /** Function to do initialization based on topology and/or parameter values. */
+ sel_initfunc init;
+ /** Function to initialize output data structure. */
+ sel_outinitfunc outinit;
+ /** Function to free the internal data. */
+ sel_freefunc free;
+
+ /** Function to initialize the calculation for a new frame. */
+ sel_framefunc init_frame;
+ /** Function to evaluate the value. */
+ sel_updatefunc update;
+ /** Function to evaluate the value using positions. */
+ sel_updatefunc_pos pupdate;
+
+ /** Help data for the method. */
+ gmx_ana_selmethod_help_t help;
+} gmx_ana_selmethod_t;
+
+/** Registers a selection method. */
+int
+gmx_ana_selmethod_register(struct gmx_sel_symtab_t *symtab,
+ const char *name, gmx_ana_selmethod_t *method);
+/** Registers all selection methods in the library. */
+int
+gmx_ana_selmethod_register_defaults(struct gmx_sel_symtab_t *symtab);
+
+/** Finds a parameter from a selection method by name. */
+gmx_ana_selparam_t *
+gmx_ana_selmethod_find_param(const char *name, gmx_ana_selmethod_t *method);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief API for handling parameters used in selections.
+ *
+ * There should be no need to use the data structures or call the
+ * functions in this file directly unless implementing a custom selection
+ * method.
+ *
+ * More details can be found on the page discussing
+ * \ref page_module_selection_custom "custom selection methods".
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELPARAM_H
+#define GMX_SELECTION_SELPARAM_H
+
+#include "indexutil.h"
+#include "selvalue.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*! \name Parameter flags
+ * \anchor selparam_flags
+ */
+/*@{*/
+/*! \brief
+ * This flag is set if the user has provided the parameter.
+ *
+ * This flag is set automatically, and should not be set by the user.
+ */
+#define SPAR_SET 1
+/*! \brief
+ * If not set, an error is reported if the parameter is not specified by the
+ * user.
+ */
+#define SPAR_OPTIONAL 2
+/*! \brief
+ * If set, the parameter value can be dynamic, i.e., be different for
+ * different frames.
+ *
+ * If set, the parameter value should only be accessed in the update function
+ * of \c gmx_ana_selmethod_t.
+ * The flag is cleared before sel_initfunc() if the value provided is actually
+ * static.
+ */
+#define SPAR_DYNAMIC 4
+/*! \brief
+ * If set, the parameter value is parsed into sorted ranges.
+ *
+ * Can only be specified for integer parameters.
+ * If specified, the value of the parameter (\c gmx_ana_selparam_t::val)
+ * consists of sets of two integers, each specifying a range.
+ * The values give the endpoints of the ranges (inclusive).
+ * The ranges are sorted and overlapping/continuous ranges are merged into
+ * a single range to minimize the number of ranges.
+ *
+ * If this flag is specified, \c gmx_ana_selparam_t::nval gives the number of
+ * ranges. \p gmx_ana_selparam_t::nval should be 1 or \ref SPAR_VARNUM should be
+ * specified; other values would lead to unpredictable behavior.
+ */
+#define SPAR_RANGES 8
+/*! \brief
+ * If set, the parameter can have any number of values.
+ *
+ * If specified, the data pointer in \c gmx_ana_selparam_t::val should be NULL;
+ * the memory is allocated by the parameter parser.
+ * The implementation of the method should ensure that the pointer to the
+ * allocated memory is stored somewhere in sel_initfunc();
+ * otherwise, the memory is lost.
+ *
+ * The initial value of \c gmx_ana_selparam_t::nval is not used with this flag.
+ * Instead, it will give the number of actual values provided by the user
+ * after the parameters have been parsed.
+ * For consistency, it should be initialized to -1.
+ *
+ * Cannot be combined with \ref GROUP_VALUE parameters.
+ */
+#define SPAR_VARNUM 16
+/*! \brief
+ * If set, the parameter can have a separate value for each atom.
+ *
+ * The flag is cleared before sel_initfunc() if the value provided is actually
+ * a single value.
+ *
+ * Cannot be combined with \ref POS_VALUE or \ref GROUP_VALUE parameters.
+ */
+#define SPAR_ATOMVAL 32
+/*! \brief
+ * If set, the parameter takes one of a set of predefined strings.
+ *
+ * Can only be specified for a \ref STR_VALUE parameter that takes a single
+ * string.
+ * The data pointer in \c gmx_ana_selparam_t::val should be initialized into an
+ * array of strings such that the first and last elements are NULL, and the
+ * rest give the possible values. For optional values, the second element in
+ * the array should give the default value. The string given by the user is
+ * matched against the beginnings of the given strings, and if a unique match
+ * is found, the first pointer in the array will be initialized to point to
+ * the matching string.
+ * The data pointer can be initialized as a static array; duplication of the
+ * array for multiple instances of the same method is automatically taken care
+ * of.
+ */
+#define SPAR_ENUMVAL 128
+/*@}*/
+
+/*! \brief
+ * Describes a single parameter for a selection method.
+ */
+typedef struct gmx_ana_selparam_t
+{
+ /** Name of the parameter. */
+ const char *name;
+ /*! \brief
+ * The parameter value.
+ *
+ * Type \ref NO_VALUE can be used to define a boolean parameter.
+ * The number of values should be 0 for boolean parameters.
+ *
+ * The value pointer be initialized to NULL in the definition of a
+ * \c gmx_ana_selmethod_t and initialized in the
+ * \c gmx_ana_selmethod_t::init_data call
+ * (see sel_datafunc()).
+ * However, if \ref SPAR_VARNUM is provided and the parameter is not
+ * \ref POS_VALUE, this field should not be initialized. Instead,
+ * sufficient memory is allocated automatically and the pointer should be
+ * stored in \c gmx_ana_selmethod_t::init
+ * (see sel_initfunc()).
+ *
+ * The values cannot be accessed outside these two functions: the compiler
+ * makes a copy of the parameter structure for each instance of the
+ * method, and the original parameter array is not changed.
+ */
+ gmx_ana_selvalue_t val;
+ /*! \brief
+ * Pointer to store the number of values.
+ *
+ * If not NULL, the number of values for the parameter is stored in the
+ * pointed value.
+ * Should be specified if \ref SPAR_VARNUM and \ref SPAR_DYNAMIC are both
+ * set.
+ *
+ * Should be initialized to NULL in the definition a \c gmx_ana_selmethod_t
+ * and initialized in sel_datafunc().
+ */
+ int *nvalptr;
+ /*! \brief
+ * Flags that alter the way the parameter is parsed/handled.
+ *
+ * See \ref selparam_flags for allowed values.
+ */
+ int flags;
+} gmx_ana_selparam_t;
+
+/** Finds a parameter from an array by name. */
+gmx_ana_selparam_t *
+gmx_ana_selparam_find(const char *name, int nparam, gmx_ana_selparam_t *param);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in selvalue.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <smalloc.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selvalue.h"
+
+/*!
+ * \param[out] val Output structure
+ *
+ * The type of \p val is not touched.
+ * Any contents of \p val are discarded without freeing.
+ */
+void
+_gmx_selvalue_clear(gmx_ana_selvalue_t *val)
+{
+ val->nr = 0;
+ val->u.ptr = NULL;
+ val->nalloc = 0;
+}
+
+/*!
+ * \param[in,out] val Value structure to allocate.
+ * \param[in] n Maximum number of values needed.
+ * \returns Zero on success.
+ *
+ * Reserves memory for the values within \p val to store at least \p n values,
+ * of the type specified in the \p val structure.
+ *
+ * If the type is \ref POS_VALUE or \ref GROUP_VALUE, memory is reserved for
+ * the data structures, but no memory is reserved inside these newly allocated
+ * data structures.
+ * Similarly, for \ref STR_VALUE values, the pointers are set to NULL.
+ * For other values, the memory is uninitialized.
+ */
+int
+_gmx_selvalue_reserve(gmx_ana_selvalue_t *val, int n)
+{
+ int i;
+
+ if (val->nalloc == -1)
+ {
+ return 0;
+ }
+
+ if (!val->u.ptr || val->nalloc < n)
+ {
+ switch (val->type)
+ {
+ case INT_VALUE: srenew(val->u.i, n); break;
+ case REAL_VALUE: srenew(val->u.r, n); break;
+ case STR_VALUE:
+ srenew(val->u.s, n);
+ for (i = val->nalloc; i < n; ++i)
+ {
+ val->u.s[i] = NULL;
+ }
+ break;
+ case POS_VALUE:
+ srenew(val->u.p, n);
+ for (i = val->nalloc; i < n; ++i)
+ {
+ gmx_ana_pos_clear(&val->u.p[i]);
+ }
+ break;
+ case GROUP_VALUE:
+ srenew(val->u.g, n);
+ for (i = val->nalloc; i < n; ++i)
+ {
+ gmx_ana_index_clear(&val->u.g[i]);
+ }
+ break;
+ case NO_VALUE: break;
+ }
+ val->nalloc = n;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in,out] val Value structure to allocate.
+ * \param[in] ptr Pointer where the values should be stored.
+ * \returns Zero on success.
+ *
+ * Automatic memory management is disabled for \p ptr, unless \p ptr is NULL.
+ */
+int
+_gmx_selvalue_setstore(gmx_ana_selvalue_t *val, void *ptr)
+{
+ val->u.ptr = ptr;
+ val->nalloc = (ptr ? -1 : 0);
+ return 0;
+}
+
+/*!
+ * \param[in,out] val Value structure to allocate.
+ * \param[in] ptr Pointer where the values should be stored.
+ * \param[in] nalloc Number of values allocated for \p ptr.
+ * \returns Zero on success.
+ */
+int
+_gmx_selvalue_setstore_alloc(gmx_ana_selvalue_t *val, void *ptr, int nalloc)
+{
+ val->u.ptr = ptr;
+ val->nalloc = nalloc;
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares ::gmx_ana_selvalue_t.
+ *
+ * There should be no need to use the data structures in this file directly
+ * unless implementing a custom selection routine.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELVALUE_H
+#define GMX_SELECTION_SELVALUE_H
+
+#include "../legacyheaders/types/simple.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Defines the value type of a different selection objects. */
+typedef enum
+{
+ NO_VALUE, /**< No value; either an error condition or an gmx_boolean
+ parameter. */
+ INT_VALUE, /**< One or more integer values. */
+ REAL_VALUE, /**< One or more real values. */
+ STR_VALUE, /**< One or more string values. */
+ POS_VALUE, /**< One or more position values. */
+ GROUP_VALUE /**< One group of atoms. */
+} e_selvalue_t;
+
+/*! \brief
+ * Describes a value of a selection expression or of a selection method
+ * parameter.
+ *
+ * Which field in the union is used depends on the \p type.
+ */
+typedef struct gmx_ana_selvalue_t
+{
+ /** Type of the value. */
+ e_selvalue_t type;
+ /*! \brief
+ * Number of values in the array pointed by the union.
+ *
+ * Note that for position and group values, it is the number of
+ * data structures in the array, not the number of positions or
+ * the number of atoms in the group.
+ */
+ int nr;
+ /** Pointer to the value. */
+ union {
+ /*! \brief
+ * Generic pointer for operations that do not need type information.
+ *
+ * Needs to be the first member to be able to use initialized arrays.
+ */
+ void *ptr;
+ /** Integer value(s) (type \ref INT_VALUE). */
+ int *i;
+ /** Real value(s) (type \ref REAL_VALUE). */
+ real *r;
+ /** String value(s) (type \ref STR_VALUE). */
+ char **s;
+ /** Structure for the position value(s) (type \ref POS_VALUE). */
+ struct gmx_ana_pos_t *p;
+ /** Group value (type \ref GROUP_VALUE). */
+ struct gmx_ana_index_t *g;
+ /** Boolean value (only parameters of type \ref NO_VALUE); */
+ gmx_bool *b;
+ } u;
+ /*! \brief
+ * Number of elements allocated for the value array.
+ */
+ int nalloc;
+} gmx_ana_selvalue_t;
+
+/** Initializes an empty selection value structure. */
+void
+_gmx_selvalue_clear(gmx_ana_selvalue_t *val);
+/** Reserve memory for storing selection values. */
+int
+_gmx_selvalue_reserve(gmx_ana_selvalue_t *val, int n);
+/** Sets the memory for storing selection values. */
+int
+_gmx_selvalue_setstore(gmx_ana_selvalue_t *val, void *ptr);
+/** Sets the memory for storing selection values and marks it for automatic freeing. */
+int
+_gmx_selvalue_setstore_alloc(gmx_ana_selvalue_t *val, void *ptr, int nalloc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements internal selection method for comparison expressions.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <maths.h>
+#include <macros.h>
+#include <smalloc.h>
+#include <gmx_fatal.h>
+
+#include "gromacs/selection/selmethod.h"
+
+/** Defines the comparison operator for comparison expressions. */
+typedef enum
+{
+ CMP_INVALID, /**< Indicates an error */
+ CMP_LESS, /**< '<' */
+ CMP_LEQ, /**< '<=' */
+ CMP_GTR, /**< '>' */
+ CMP_GEQ, /**< '>=' */
+ CMP_EQUAL, /**< '==' */
+ CMP_NEQ /**< '!=' */
+} e_comparison_t;
+
+/** The operand has a single value. */
+#define CMP_SINGLEVAL 1
+/** The operand value is dynamic. */
+#define CMP_DYNAMICVAL 2
+/** The value is real. */
+#define CMP_REALVAL 4
+/** The integer array is allocated. */
+#define CMP_ALLOCINT 16
+/** The real array is allocated. */
+#define CMP_ALLOCREAL 32
+
+/*! \internal \brief
+ * Data structure for comparison expression operand values.
+ */
+typedef struct
+{
+ /** Flags that describe the type of the operand. */
+ int flags;
+ /** (Array of) integer value(s). */
+ int *i;
+ /** (Array of) real value(s). */
+ real *r;
+} t_compare_value;
+
+/*! \internal \brief
+ * Data structure for comparison expression evaluation.
+ */
+typedef struct
+{
+ /** Comparison operator as a string. */
+ char *cmpop;
+ /** Comparison operator type. */
+ e_comparison_t cmpt;
+ /** Left value. */
+ t_compare_value left;
+ /** Right value. */
+ t_compare_value right;
+} t_methoddata_compare;
+
+/** Allocates data for comparison expression evaluation. */
+static void *
+init_data_compare(int npar, gmx_ana_selparam_t *param);
+/** Initializes data for comparison expression evaluation. */
+static int
+init_compare(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Frees the memory allocated for comparison expression evaluation. */
+static void
+free_data_compare(void *data);
+/** Evaluates comparison expressions. */
+static int
+evaluate_compare(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+
+/** Parameters for comparison expression evaluation. */
+static gmx_ana_selparam_t smparams_compare[] = {
+ {"int1", {INT_VALUE, -1, {NULL}}, NULL,
+ SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
+ {"real1", {REAL_VALUE, -1, {NULL}}, NULL,
+ SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
+ {"op", {STR_VALUE, 1, {NULL}}, NULL, 0},
+ {"int2", {INT_VALUE, -1, {NULL}}, NULL,
+ SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
+ {"real2", {REAL_VALUE, -1, {NULL}}, NULL,
+ SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
+};
+
+/** \internal Selection method data for comparison expression evaluation. */
+gmx_ana_selmethod_t sm_compare = {
+ "cmp", GROUP_VALUE, SMETH_SINGLEVAL,
+ asize(smparams_compare), smparams_compare,
+ &init_data_compare,
+ NULL,
+ &init_compare,
+ NULL,
+ &free_data_compare,
+ NULL,
+ &evaluate_compare,
+ NULL,
+ {NULL, 0, NULL},
+};
+
+/*! \brief
+ * Returns a \c e_comparison_t value corresponding to an operator.
+ *
+ * \param[in] str String to process.
+ * \returns The comparison type corresponding to the first one or two
+ * characters of \p str.
+ *
+ * \p str can contain any number of characters; only the first two
+ * are used.
+ * If the beginning of \p str does not match any of the recognized types,
+ * \ref CMP_INVALID is returned.
+ */
+static e_comparison_t
+comparison_type(char *str)
+{
+ switch (str[0])
+ {
+ case '<': return (str[1] == '=') ? CMP_LEQ : CMP_LESS;
+ case '>': return (str[1] == '=') ? CMP_GEQ : CMP_GTR;
+ case '=': return (str[1] == '=') ? CMP_EQUAL : CMP_INVALID;
+ case '!': return (str[1] == '=') ? CMP_NEQ : CMP_INVALID;
+ }
+ return CMP_INVALID;
+}
+
+/*! \brief
+ * Returns a string corresponding to a \c e_comparison_t value.
+ *
+ * \param[in] cmpt Comparison type to convert.
+ * \returns Pointer to a string that corresponds to \p cmpt.
+ *
+ * The return value points to a string constant and should not be \p free'd.
+ *
+ * The function returns NULL if \p cmpt is not one of the valid values.
+ */
+static const char *
+comparison_type_str(e_comparison_t cmpt)
+{
+ switch (cmpt)
+ {
+ case CMP_INVALID: return "INVALID"; break;
+ case CMP_LESS: return "<"; break;
+ case CMP_LEQ: return "<="; break;
+ case CMP_GTR: return ">"; break;
+ case CMP_GEQ: return ">="; break;
+ case CMP_EQUAL: return "=="; break;
+ case CMP_NEQ: return "!="; break;
+ }
+ return NULL;
+}
+
+/*!
+ * \param[in] fp File to receive the output.
+ * \param[in] data Should point to a \c t_methoddata_compare.
+ */
+void
+_gmx_selelem_print_compare_info(FILE *fp, void *data)
+{
+ t_methoddata_compare *d = (t_methoddata_compare *)data;
+
+ fprintf(fp, " \"");
+ /* Print the left value */
+ if ((d->left.flags & CMP_SINGLEVAL) && !(d->left.flags & CMP_DYNAMICVAL))
+ {
+ if (d->left.flags & CMP_REALVAL)
+ {
+ fprintf(fp, "%f ", d->left.r[0]);
+ }
+ else
+ {
+ fprintf(fp, "%d ", d->left.i[0]);
+ }
+ }
+ /* Print the operator */
+ if (d->cmpt != CMP_INVALID)
+ {
+ fprintf(fp, "%s", comparison_type_str(d->cmpt));
+ }
+ else
+ {
+ fprintf(fp, "%s", d->cmpop);
+ }
+ /* Print the right value */
+ if ((d->right.flags & CMP_SINGLEVAL) && !(d->right.flags & CMP_DYNAMICVAL))
+ {
+ if (d->right.flags & CMP_REALVAL)
+ {
+ fprintf(fp, " %f", d->right.r[0]);
+ }
+ else
+ {
+ fprintf(fp, " %d", d->right.i[0]);
+ }
+ }
+ fprintf(fp, "\"");
+}
+
+/*!
+ * \param[in] npar Not used (should be 5).
+ * \param[in,out] param Method parameters (should point to a copy of
+ * \ref smparams_compare).
+ * \returns Pointer to the allocated data (\c t_methoddata_compare).
+ *
+ * Allocates memory for a \c t_methoddata_compare structure.
+ */
+static void *
+init_data_compare(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_compare *data;
+
+ snew(data, 1);
+ param[2].val.u.s = &data->cmpop;
+ return data;
+}
+
+/* \brief
+ * Reverses a comparison operator.
+ *
+ * \param[in] type Comparison operator to reverse.
+ * \returns The correct comparison operator that equals \p type when the
+ * left and right sides are interchanged.
+ */
+static e_comparison_t
+reverse_comparison_type(e_comparison_t type)
+{
+ switch (type)
+ {
+ case CMP_LESS: return CMP_GTR;
+ case CMP_LEQ: return CMP_GEQ;
+ case CMP_GTR: return CMP_LESS;
+ case CMP_GEQ: return CMP_LEQ;
+ default: break;
+ }
+ return type;
+}
+
+/*! \brief
+ * Initializes the value storage for comparison expression.
+ *
+ * \param[out] val Value structure to initialize.
+ * \param[in] param Parameters to use for initialization.
+ * \returns The number of values provided for the value, 0 on error.
+ */
+static int
+init_comparison_value(t_compare_value *val, gmx_ana_selparam_t param[2])
+{
+ int n;
+
+ val->flags = 0;
+ if (param[0].flags & SPAR_SET)
+ {
+ val->flags |= (param[0].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
+ val->flags |= !(param[0].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL : 0;
+ n = param[0].val.nr;
+ val->i = param[0].val.u.i;
+ }
+ else if (param[1].flags & SPAR_SET)
+ {
+ val->flags |= (param[1].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
+ val->flags |= !(param[1].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL : 0;
+ val->flags |= CMP_REALVAL;
+ n = param[1].val.nr;
+ val->r = param[1].val.u.r;
+ }
+ else
+ {
+ n = 0;
+ val->i = NULL;
+ val->r = NULL;
+ }
+ return n;
+}
+
+/* \brief
+ * Converts an integer value to floating point.
+ *
+ * \param[in] n Number of values in the \p val->u array.
+ * \param[in,out] val Value to convert.
+ */
+static void
+convert_int_real(int n, t_compare_value *val)
+{
+ int i;
+ real *rv;
+
+ snew(rv, n);
+ for (i = 0; i < n; ++i)
+ {
+ rv[i] = (real)val->i[i];
+ }
+ /* Free the previous value if one is present. */
+ sfree(val->r);
+ val->r = rv;
+ val->flags |= CMP_REALVAL | CMP_ALLOCREAL;
+}
+
+/* \brief
+ * Converts a floating point value to integer.
+ *
+ * \param[in] n Number of values in the \p val->u array.
+ * \param[in,out] val Value to convert.
+ * \param[in] cmpt Comparison operator type.
+ * \param[in] bRight TRUE if \p val appears on the right hand size of
+ * \p cmpt.
+ * \returns 0 on success, EINVAL on error.
+ *
+ * The values are rounded such that the same comparison operator can be used.
+ */
+static int
+convert_real_int(int n, t_compare_value *val, e_comparison_t cmpt, gmx_bool bRight)
+{
+ int i;
+ int *iv;
+
+ if (!bRight)
+ {
+ cmpt = reverse_comparison_type(cmpt);
+ }
+ snew(iv, n);
+ /* Round according to the comparison type */
+ for (i = 0; i < n; ++i)
+ {
+ switch (cmpt)
+ {
+ case CMP_LESS:
+ case CMP_GEQ:
+ iv[i] = (int)ceil(val->r[i]);
+ break;
+ case CMP_GTR:
+ case CMP_LEQ:
+ iv[i] = (int)floor(val->r[i]);
+ break;
+ case CMP_EQUAL:
+ case CMP_NEQ:
+ fprintf(stderr, "comparing equality an integer expression and a real value\n");
+ sfree(iv);
+ return EINVAL;
+ case CMP_INVALID: /* Should not be reached */
+ gmx_bug("internal error");
+ sfree(iv);
+ return EINVAL;
+ }
+ }
+ /* Free the previous value if one is present. */
+ sfree(val->i);
+ val->i = iv;
+ val->flags &= ~CMP_REALVAL;
+ val->flags |= CMP_ALLOCINT;
+ return 0;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used (should be 5).
+ * \param[in] param Method parameters (should point to \ref smparams_compare).
+ * \param[in] data Should point to a \c t_methoddata_compare.
+ * \returns 0 if the input data is valid, -1 on error.
+ */
+static int
+init_compare(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_compare *d = (t_methoddata_compare *)data;
+ int n1, n2;
+
+ /* Store the values */
+ n1 = init_comparison_value(&d->left, ¶m[0]);
+ n2 = init_comparison_value(&d->right, ¶m[3]);
+ if (n1 == 0 || n2 == 0)
+ {
+ gmx_bug("one of the values for comparison missing");
+ return -1;
+ }
+ /* Store the comparison type */
+ d->cmpt = comparison_type(d->cmpop);
+ if (d->cmpt == CMP_INVALID)
+ {
+ gmx_bug("invalid comparison type");
+ return -1;
+ }
+ /* Convert the values to the same type */
+ if ((d->left.flags & CMP_REALVAL) && !(d->right.flags & CMP_REALVAL))
+ {
+ if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
+ {
+ /* Nothing can be done */
+ }
+ else if (!(d->right.flags & CMP_DYNAMICVAL))
+ {
+ convert_int_real(n2, &d->right);
+ }
+ else /* d->left is static */
+ {
+ if (convert_real_int(n1, &d->left, d->cmpt, FALSE))
+ {
+ return -1;
+ }
+ }
+ }
+ else if (!(d->left.flags & CMP_REALVAL) && (d->right.flags & CMP_REALVAL))
+ {
+ if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
+ {
+ /* Reverse the sides to place the integer on the right */
+ int flags;
+ d->left.r = d->right.r;
+ d->right.r = NULL;
+ d->right.i = d->left.i;
+ d->left.i = NULL;
+ flags = d->left.flags;
+ d->left.flags = d->right.flags;
+ d->right.flags = flags;
+ d->cmpt = reverse_comparison_type(d->cmpt);
+ }
+ else if (!(d->left.flags & CMP_DYNAMICVAL))
+ {
+ convert_int_real(n1, &d->left);
+ }
+ else /* d->right is static */
+ {
+ if (convert_real_int(n2, &d->right, d->cmpt, TRUE))
+ {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \c t_methoddata_compare).
+ *
+ * Frees the memory allocated for \c t_methoddata_compare.
+ */
+static void
+free_data_compare(void *data)
+{
+ t_methoddata_compare *d = (t_methoddata_compare *)data;
+
+ sfree(d->cmpop);
+ if (d->left.flags & CMP_ALLOCINT)
+ {
+ sfree(d->left.i);
+ }
+ if (d->left.flags & CMP_ALLOCREAL)
+ {
+ sfree(d->left.r);
+ }
+ if (d->right.flags & CMP_ALLOCINT)
+ {
+ sfree(d->right.i);
+ }
+ if (d->right.flags & CMP_ALLOCREAL)
+ {
+ sfree(d->right.r);
+ }
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Not used.
+ * \param[in] pbc Not used.
+ * \param[in] g Evaluation index group.
+ * \param[out] out Output data structure (\p out->u.g is used).
+ * \param[in] data Should point to a \c t_methoddata_compare.
+ * \returns 0 for success.
+ */
+static int
+evaluate_compare_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_compare *d = (t_methoddata_compare *)data;
+ int i, i1, i2, ig;
+ int a, b;
+ gmx_bool bAccept;
+
+ for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
+ {
+ a = d->left.i[i1];
+ b = d->right.i[i2];
+ bAccept = FALSE;
+ switch (d->cmpt)
+ {
+ case CMP_INVALID: break;
+ case CMP_LESS: bAccept = a < b; break;
+ case CMP_LEQ: bAccept = a <= b; break;
+ case CMP_GTR: bAccept = a > b; break;
+ case CMP_GEQ: bAccept = a >= b; break;
+ case CMP_EQUAL: bAccept = a == b; break;
+ case CMP_NEQ: bAccept = a != b; break;
+ }
+ if (bAccept)
+ {
+ out->u.g->index[ig++] = g->index[i];
+ }
+ if (!(d->left.flags & CMP_SINGLEVAL))
+ {
+ ++i1;
+ }
+ if (!(d->right.flags & CMP_SINGLEVAL))
+ {
+ ++i2;
+ }
+ }
+ out->u.g->isize = ig;
+ return 0;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Not used.
+ * \param[in] pbc Not used.
+ * \param[in] g Evaluation index group.
+ * \param[out] out Output data structure (\p out->u.g is used).
+ * \param[in] data Should point to a \c t_methoddata_compare.
+ * \returns 0 for success.
+ */
+static int
+evaluate_compare_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_compare *d = (t_methoddata_compare *)data;
+ int i, i1, i2, ig;
+ real a, b;
+ gmx_bool bAccept;
+
+ for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
+ {
+ a = d->left.r[i1];
+ b = (d->right.flags & CMP_REALVAL) ? d->right.r[i2] : d->right.i[i2];
+ bAccept = FALSE;
+ switch (d->cmpt)
+ {
+ case CMP_INVALID: break;
+ case CMP_LESS: bAccept = a < b; break;
+ case CMP_LEQ: bAccept = a <= b; break;
+ case CMP_GTR: bAccept = a > b; break;
+ case CMP_GEQ: bAccept = a >= b; break;
+ case CMP_EQUAL: bAccept = gmx_within_tol(a, b, GMX_REAL_EPS); break;
+ case CMP_NEQ: bAccept = !gmx_within_tol(a, b, GMX_REAL_EPS); break;
+ }
+ if (bAccept)
+ {
+ out->u.g->index[ig++] = g->index[i];
+ }
+ if (!(d->left.flags & CMP_SINGLEVAL))
+ {
+ ++i1;
+ }
+ if (!(d->right.flags & CMP_SINGLEVAL))
+ {
+ ++i2;
+ }
+ }
+ out->u.g->isize = ig;
+ return 0;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Not used.
+ * \param[in] pbc Not used.
+ * \param[in] g Evaluation index group.
+ * \param[out] out Output data structure (\p out->u.g is used).
+ * \param[in] data Should point to a \c t_methoddata_compare.
+ * \returns 0 for success.
+ */
+static int
+evaluate_compare(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_compare *d = (t_methoddata_compare *)data;
+
+ if (!((d->left.flags | d->right.flags) & CMP_REALVAL))
+ {
+ return evaluate_compare_int(top, fr, pbc, g, out, data);
+ }
+ else
+ {
+ return evaluate_compare_real(top, fr, pbc, g, out, data);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements distance-based selection methods.
+ *
+ * This file implements the \p distance, \p mindistance and \p within
+ * selection methods.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <macros.h>
+#include <pbc.h>
+#include <smalloc.h>
+#include <vec.h>
+
+#include "gromacs/selection/nbsearch.h"
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+
+/*! \internal \brief
+ * Data structure for distance-based selection method.
+ *
+ * The same data structure is used by all the distance-based methods.
+ */
+typedef struct
+{
+ /** Cutoff distance. */
+ real cutoff;
+ /** Positions of the reference points. */
+ gmx_ana_pos_t p;
+ /** Neighborhood search data. */
+ gmx_ana_nbsearch_t *nb;
+} t_methoddata_distance;
+
+/** Allocates data for distance-based selection methods. */
+static void *
+init_data_common(int npar, gmx_ana_selparam_t *param);
+/** Initializes a distance-based selection method. */
+static int
+init_common(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Frees the data allocated for a distance-based selection method. */
+static void
+free_data_common(void *data);
+/** Initializes the evaluation of a distance-based within selection method for a frame. */
+static int
+init_frame_common(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
+/** Evaluates the \p distance selection method. */
+static int
+evaluate_distance(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p within selection method. */
+static int
+evaluate_within(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
+
+/** Parameters for the \p distance selection method. */
+static gmx_ana_selparam_t smparams_distance[] = {
+ {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
+ {"from", {POS_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
+};
+
+/** Parameters for the \p mindistance selection method. */
+static gmx_ana_selparam_t smparams_mindistance[] = {
+ {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
+ {"from", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+};
+
+/** Parameters for the \p within selection method. */
+static gmx_ana_selparam_t smparams_within[] = {
+ {NULL, {REAL_VALUE, 1, {NULL}}, NULL, 0},
+ {"of", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+};
+
+/** Help text for the distance selection methods. */
+static const char *help_distance[] = {
+ "DISTANCE-BASED SELECTION KEYWORDS[PAR]",
+
+ "[TT]distance from POS [cutoff REAL][tt][BR]",
+ "[TT]mindistance from POS_EXPR [cutoff REAL][tt][BR]",
+ "[TT]within REAL of POS_EXPR[tt][PAR]",
+
+ "[TT]distance[tt] and [TT]mindistance[tt] calculate the distance from the",
+ "given position(s), the only difference being in that [TT]distance[tt]",
+ "only accepts a single position, while any number of positions can be",
+ "given for [TT]mindistance[tt], which then calculates the distance to the",
+ "closest position.",
+ "[TT]within[tt] directly selects atoms that are within [TT]REAL[tt] of",
+ "[TT]POS_EXPR[tt].[PAR]",
+
+ "For the first two keywords, it is possible to specify a cutoff to speed",
+ "up the evaluation: all distances above the specified cutoff are",
+ "returned as equal to the cutoff.",
+ "Currently, this does nothing, but in the future, it allows the use of",
+ "grid-based neighborhood search techniques.",
+};
+
+/** \internal Selection method data for the \p distance method. */
+gmx_ana_selmethod_t sm_distance = {
+ "distance", REAL_VALUE, SMETH_DYNAMIC,
+ asize(smparams_distance), smparams_distance,
+ &init_data_common,
+ NULL,
+ &init_common,
+ NULL,
+ &free_data_common,
+ &init_frame_common,
+ NULL,
+ &evaluate_distance,
+ {"distance from POS [cutoff REAL]", asize(help_distance), help_distance},
+};
+
+/** \internal Selection method data for the \p distance method. */
+gmx_ana_selmethod_t sm_mindistance = {
+ "mindistance", REAL_VALUE, SMETH_DYNAMIC,
+ asize(smparams_mindistance), smparams_mindistance,
+ &init_data_common,
+ NULL,
+ &init_common,
+ NULL,
+ &free_data_common,
+ &init_frame_common,
+ NULL,
+ &evaluate_distance,
+ {"mindistance from POS_EXPR [cutoff REAL]", asize(help_distance), help_distance},
+};
+
+/** \internal Selection method data for the \p within method. */
+gmx_ana_selmethod_t sm_within = {
+ "within", GROUP_VALUE, SMETH_DYNAMIC,
+ asize(smparams_within), smparams_within,
+ &init_data_common,
+ NULL,
+ &init_common,
+ NULL,
+ &free_data_common,
+ &init_frame_common,
+ NULL,
+ &evaluate_within,
+ {"within REAL of POS_EXPR", asize(help_distance), help_distance},
+};
+
+/*!
+ * \param[in] npar Not used (should be 2).
+ * \param[in,out] param Method parameters (should point to one of the distance
+ * parameter arrays).
+ * \returns Pointer to the allocated data (\c t_methoddata_distance).
+ *
+ * Allocates memory for a \c t_methoddata_distance structure and
+ * initializes the parameter as follows:
+ * - the first parameter defines the value for
+ * \c t_methoddata_distance::cutoff.
+ * - the second parameter defines the reference positions and the value is
+ * stored in \c t_methoddata_distance::p.
+ */
+static void *
+init_data_common(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_distance *data;
+
+ snew(data, 1);
+ data->cutoff = -1;
+ param[0].val.u.r = &data->cutoff;
+ param[1].val.u.p = &data->p;
+ return data;
+}
+
+/*!
+ * \param top Not used.
+ * \param npar Not used (should be 2).
+ * \param param Method parameters (should point to one of the distance
+ * parameter arrays).
+ * \param data Pointer to \c t_methoddata_distance to initialize.
+ * \returns 0 on success, a non-zero error code on failure.
+ *
+ * Initializes the neighborhood search data structure
+ * (\c t_methoddata_distance::nb).
+ * Also checks that the cutoff is valid.
+ */
+static int
+init_common(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_distance *d = (t_methoddata_distance *)data;
+
+ if ((param[0].flags & SPAR_SET) && d->cutoff <= 0)
+ {
+ fprintf(stderr, "error: distance cutoff should be > 0");
+ return -1;
+ }
+ return gmx_ana_nbsearch_create(&d->nb, d->cutoff, d->p.nr);
+}
+
+/*!
+ * \param data Data to free (should point to a \c t_methoddata_distance).
+ *
+ * Frees the memory allocated for \c t_methoddata_distance::xref and
+ * \c t_methoddata_distance::nb.
+ */
+static void
+free_data_common(void *data)
+{
+ t_methoddata_distance *d = (t_methoddata_distance *)data;
+
+ if (d->nb)
+ {
+ gmx_ana_nbsearch_free(d->nb);
+ }
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Current frame.
+ * \param[in] pbc PBC structure.
+ * \param data Should point to a \c t_methoddata_distance.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Initializes the neighborhood search for the current frame.
+ */
+static int
+init_frame_common(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
+{
+ t_methoddata_distance *d = (t_methoddata_distance *)data;
+
+ return gmx_ana_nbsearch_pos_init(d->nb, pbc, &d->p);
+}
+
+/*!
+ * See sel_updatefunc_pos() for description of the parameters.
+ * \p data should point to a \c t_methoddata_distance.
+ *
+ * Calculates the distance of each position from \c t_methoddata_distance::p
+ * and puts them in \p out->u.r.
+ */
+static int
+evaluate_distance(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_distance *d = (t_methoddata_distance *)data;
+ int b, i;
+ real n;
+
+ out->nr = pos->g->isize;
+ for (b = 0; b < pos->nr; ++b)
+ {
+ n = gmx_ana_nbsearch_pos_mindist(d->nb, pos, b);
+ for (i = pos->m.mapb.index[b]; i < pos->m.mapb.index[b+1]; ++i)
+ {
+ out->u.r[i] = n;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_distance.
+ *
+ * Finds the atoms that are closer than the defined cutoff to
+ * \c t_methoddata_distance::xref and puts them in \p out.g.
+ */
+static int
+evaluate_within(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_distance *d = (t_methoddata_distance *)data;
+ int b;
+
+ out->u.g->isize = 0;
+ for (b = 0; b < pos->nr; ++b)
+ {
+ if (gmx_ana_nbsearch_pos_is_within(d->nb, pos, b))
+ {
+ gmx_ana_pos_append(NULL, out->u.g, pos, b, 0);
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \page page_module_selection_insolidangle Selection method: insolidangle
+ *
+ * This method selects a subset of particles that are located in a solid
+ * angle defined by a center and a set of points.
+ * The solid angle is constructed as a union of small cones whose axis
+ * goes through the center and a point.
+ * So there's such a cone for each position, and a
+ * point is in the solid angle if it lies within any of these cones.
+ * The width of the cones can be adjusted.
+ *
+ * \internal
+ *
+ * The method is implemented by partitioning the surface of the unit sphere
+ * into bins using the polar coordinates \f$(\theta, \phi)\f$.
+ * The partitioning is always uniform in the zenith angle \f$\theta\f$,
+ * while the partitioning in the azimuthal angle \f$\phi\f$ varies.
+ * For each reference point, the unit vector from the center to the point
+ * is constructed, and it is stored in all the bins that overlap with the
+ * cone defined by the point.
+ * Bins that are completely covered by a single cone are marked as such.
+ * Checking whether a point is in the solid angle is then straightforward
+ * with this data structure: one finds the bin that corresponds to the point,
+ * and checks whether the bin is completely covered. If it is not, one
+ * additionally needs to check whether it is within the specified cutoff of
+ * any of the stored points.
+ *
+ * The above construction gives quite a lot of flexibility for constructing
+ * the bins without modifying the rest of the code.
+ * The current (quite inefficient) implementation is discussed below, but
+ * it should be optimized to get the most out of the code.
+ *
+ * The current way of constructing the bins constructs the boundaries
+ * statically: the bin size in the zenith direction is set to approximately
+ * half the angle cutoff, and the bins in the azimuthal direction have
+ * sizes such that the shortest edge of the bin is approximately equal to
+ * half the angle cutoff (for the regions close to the poles, a single bin
+ * is used).
+ * Each reference point is then added to the bins as follows:
+ * -# Find the zenith angle range that is spanned by the cone centered at the
+ * point (this is simple addition/subtraction).
+ * -# Calculate the maximal span of the cone in the azimuthal direction using
+ * the formula
+ * \f[\sin \Delta \phi_{max} = \frac{\sin \alpha}{\sin \theta}\f]
+ * (a sine formula in spherical coordinates),
+ * where \f$\alpha\f$ is the width of the cone and \f$\theta\f$ is the
+ * zenith angle of the cone center.
+ * Similarly, the zenith angle at which this extent is achieved is
+ * calculated using
+ * \f[\cos \theta_{max} = \frac{\cos \theta}{\cos \alpha}\f]
+ * (Pythagoras's theorem in spherical coordinates).
+ * -# For each zenith angle bin that is at least partially covered by the
+ * cone, calculate the span of the cone at the edges using
+ * \f[\sin^2 \frac{\Delta \phi}{2} = \frac{\sin^2 \frac{\alpha}{2} - \sin^2 \frac{\theta - \theta'}{2}}{\sin \theta \sin \theta'}\f]
+ * (distance in spherical geometry),
+ * where \f$\theta'\f$ is the zenith angle of the bin edge.
+ * -# Using the values calculated above, loop through the azimuthal bins that
+ * are partially or completely covered by the cone and update them.
+ *
+ * The total solid angle (for covered fraction calculations) is estimated by
+ * taking the total area of completely covered bins plus
+ * half the area of partially covered bins.
+ * The second one is an approximation, but should give reasonable estimates
+ * for the averages as well as in cases where the bin size is small.
+ */
+/*! \internal \file
+ * \brief
+ * Implements the \ref sm_insolidangle "insolidangle" selection method.
+ *
+ * \todo
+ * The implementation could be optimized quite a bit.
+ *
+ * \todo
+ * Move the covered fraction stuff somewhere else and make it more generic
+ * (along the lines it is handled in selection.h and trajana.h in the old C
+ * API).
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+#include <macros.h>
+#include <maths.h>
+#include <pbc.h>
+#include <physics.h>
+#include <smalloc.h>
+#include <vec.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "selelem.h"
+
+/*! \internal \brief
+ * Internal data structure for the \p insolidangle selection method.
+ *
+ * \see \c t_partition
+ */
+typedef struct
+{
+ /** Left edge of the partition. */
+ real left;
+ /** Bin index corresponding to this partition. */
+ int bin;
+} t_partition_item;
+
+/*! \internal \brief
+ * Internal data structure for the \p insolidangle selection method.
+ *
+ * Describes the surface partitioning within one slice along the zenith angle.
+ * The slice from azimuthal angle \p p[i].left to \p p[i+1].left belongs to
+ * bin \p p[i].bin.
+ */
+typedef struct
+{
+ /** Number of partition items (\p p contains \p n+1 items). */
+ int n;
+ /** Array of partition edges and corresponding bins. */
+ t_partition_item *p;
+} t_partition;
+
+/*! \internal \brief
+ * Internal data structure for the \p insolidangle selection method.
+ *
+ * Contains the reference points that partially cover a certain region on the
+ * surface of the unit sphere.
+ * If \p n is -1, the whole region described by the bin is covered.
+ */
+typedef struct
+{
+ /** Number of points in the array \p x, -1 if whole bin covered. */
+ int n;
+ /** Number of elements allocated for \p x. */
+ int n_alloc;
+ /** Array of points that partially cover the bin. */
+ rvec *x;
+} t_spheresurfacebin;
+
+/*! \internal \brief
+ * Data structure for the \p insolidangle selection method.
+ *
+ * All angle values are in the units of radians.
+ */
+typedef struct
+{
+ /** Center of the solid angle. */
+ gmx_ana_pos_t center;
+ /** Positions that span the solid angle. */
+ gmx_ana_pos_t span;
+ /** Cutoff angle. */
+ real angcut;
+ /** Estimate of the covered fraction. */
+ real cfrac;
+
+ /** Cutoff for the cosine (equals cos(angcut)). */
+ real distccut;
+ /** Bin size to be used as the target bin size when constructing the bins. */
+ real targetbinsize;
+
+ /** Number of bins in the \p tbin array. */
+ int ntbins;
+ /** Size of one bin in the zenith angle direction. */
+ real tbinsize;
+ /** Array of zenith angle slices. */
+ t_partition *tbin;
+ /** Number of elements allocated for the \p bin array. */
+ int maxbins;
+ /** Number of elements used in the \p bin array. */
+ int nbins;
+ /** Array of individual bins. */
+ t_spheresurfacebin *bin;
+} t_methoddata_insolidangle;
+
+/** Allocates data for the \p insolidangle selection method. */
+static void *
+init_data_insolidangle(int npar, gmx_ana_selparam_t *param);
+/** Initializes the \p insolidangle selection method. */
+static int
+init_insolidangle(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Sets the COM/COG data for the \p insolidangle selection method. */
+static void
+set_comg_insolidangle(gmx_ana_pos_t *pos, void *data);
+/** Frees the data allocated for the \p insolidangle selection method. */
+static void
+free_data_insolidangle(void *data);
+/** Initializes the evaluation of the \p insolidangle selection method for a frame. */
+static int
+init_frame_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
+/** Internal helper function for evaluate_insolidangle(). */
+static gmx_bool
+accept_insolidangle(rvec x, t_pbc *pbc, void *data);
+/** Evaluates the \p insolidangle selection method. */
+static int
+evaluate_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
+
+/** Calculates the distance between unit vectors. */
+static real
+sph_distc(rvec x1, rvec x2);
+/** Does a binary search on a \p t_partition to find a bin for a value. */
+static int
+find_partition_bin(t_partition *p, real value);
+/** Finds a bin that corresponds to a location on the unit sphere surface. */
+static int
+find_surface_bin(t_methoddata_insolidangle *surf, rvec x);
+/** Clears/initializes the bins on the unit sphere surface. */
+static void
+clear_surface_points(t_methoddata_insolidangle *surf);
+/** Frees memory allocated for storing the reference points in the surface bins. */
+static void
+free_surface_points(t_methoddata_insolidangle *surf);
+/** Adds a reference point to a given bin. */
+static void
+add_surface_point(t_methoddata_insolidangle *surf, int tbin, int pbin, rvec x);
+/** Marks a bin as completely covered. */
+static void
+mark_surface_covered(t_methoddata_insolidangle *surf, int tbin, int pbin);
+/** Helper function for store_surface_point() to update a single zenith angle bin. */
+static void
+update_surface_bin(t_methoddata_insolidangle *surf, int tbin,
+ real phi, real pdelta1, real pdelta2, real pdeltamax,
+ rvec x);
+/** Adds a single reference point and updates the surface bins. */
+static void
+store_surface_point(t_methoddata_insolidangle *surf, rvec x);
+/** Optimizes the surface bins for faster searching. */
+static void
+optimize_surface_points(t_methoddata_insolidangle *surf);
+/** Estimates the area covered by the reference cones. */
+static real
+estimate_covered_fraction(t_methoddata_insolidangle *surf);
+/** Checks whether a point lies within a solid angle. */
+static gmx_bool
+is_surface_covered(t_methoddata_insolidangle *surf, rvec x);
+
+/** Parameters for the \p insolidangle selection method. */
+static gmx_ana_selparam_t smparams_insolidangle[] = {
+ {"center", {POS_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
+ {"span", {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+ {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
+};
+
+/** Help text for the \p insolidangle selection method. */
+static const char *help_insolidangle[] = {
+ "SELECTING ATOMS IN A SOLID ANGLE[PAR]",
+
+ "[TT]insolidangle center POS span POS_EXPR [cutoff REAL][tt][PAR]",
+
+ "This keyword selects atoms that are within [TT]REAL[tt] degrees",
+ "(default=5) of any position in [TT]POS_EXPR[tt] as seen from [TT]POS[tt]",
+ "a position expression that evaluates to a single position), i.e., atoms",
+ "in the solid angle spanned by the positions in [TT]POS_EXPR[tt] and",
+ "centered at [TT]POS[tt].[PAR]"
+
+ "Technically, the solid angle is constructed as a union of small cones",
+ "whose tip is at [TT]POS[tt] and the axis goes through a point in",
+ "[TT]POS_EXPR[tt]. There is such a cone for each position in",
+ "[TT]POS_EXPR[tt], and point is in the solid angle if it lies within any",
+ "of these cones. The cutoff determines the width of the cones.",
+};
+
+/** \internal Selection method data for the \p insolidangle method. */
+gmx_ana_selmethod_t sm_insolidangle = {
+ "insolidangle", GROUP_VALUE, SMETH_DYNAMIC,
+ asize(smparams_insolidangle), smparams_insolidangle,
+ &init_data_insolidangle,
+ NULL,
+ &init_insolidangle,
+ NULL,
+ &free_data_insolidangle,
+ &init_frame_insolidangle,
+ NULL,
+ &evaluate_insolidangle,
+ {"insolidangle center POS span POS_EXPR [cutoff REAL]",
+ asize(help_insolidangle), help_insolidangle},
+};
+
+/*!
+ * \param[in] npar Not used (should be 3).
+ * \param[in,out] param Method parameters (should point to
+ * \ref smparams_insolidangle).
+ * \returns Pointer to the allocated data (\ref t_methoddata_insolidangle).
+ *
+ * Allocates memory for a \ref t_methoddata_insolidangle structure and
+ * initializes the parameter as follows:
+ * - \p center defines the value for t_methoddata_insolidangle::center.
+ * - \p span defines the value for t_methoddata_insolidangle::span.
+ * - \p cutoff defines the value for t_methoddata_insolidangle::angcut.
+ */
+static void *
+init_data_insolidangle(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_insolidangle *data;
+
+ snew(data, 1);
+ data->angcut = 5.0;
+ param[0].val.u.p = &data->center;
+ param[1].val.u.p = &data->span;
+ param[2].val.u.r = &data->angcut;
+ return data;
+}
+
+/*!
+ * \param top Not used.
+ * \param npar Not used.
+ * \param param Not used.
+ * \param data Pointer to \ref t_methoddata_insolidangle to initialize.
+ * \returns 0 on success, -1 on failure.
+ *
+ * Converts t_methoddata_insolidangle::angcut to radians and allocates
+ * and allocates memory for the bins used during the evaluation.
+ */
+static int
+init_insolidangle(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_insolidangle *surf = (t_methoddata_insolidangle *)data;
+ int i, c;
+
+ if (surf->angcut <= 0)
+ {
+ fprintf(stderr, "error: angle cutoff should be > 0");
+ return -1;
+ }
+
+ surf->angcut *= DEG2RAD;
+
+ surf->distccut = -cos(surf->angcut);
+ surf->targetbinsize = surf->angcut / 2;
+ surf->ntbins = (int) (M_PI / surf->targetbinsize);
+ surf->tbinsize = (180.0 / surf->ntbins)*DEG2RAD;
+
+ snew(surf->tbin, (int)(M_PI/surf->tbinsize) + 1);
+ surf->maxbins = 0;
+ for (i = 0; i < surf->ntbins; ++i)
+ {
+ c = max(sin(surf->tbinsize*i), sin(surf->tbinsize*(i+1)))
+ * M_2PI / surf->targetbinsize + 1;
+ snew(surf->tbin[i].p, c+1);
+ surf->maxbins += c;
+ }
+ surf->nbins = 0;
+ snew(surf->bin, surf->maxbins);
+
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \ref t_methoddata_insolidangle).
+ *
+ * Frees the memory allocated for \c t_methoddata_insolidangle::center and
+ * \c t_methoddata_insolidangle::span, as well as the memory for the internal
+ * bin structure.
+ */
+static void
+free_data_insolidangle(void *data)
+{
+ t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
+ int i;
+
+ if (d->tbin)
+ {
+ for (i = 0; i < d->ntbins; ++i)
+ {
+ sfree(d->tbin[i].p);
+ }
+ sfree(d->tbin);
+ }
+ free_surface_points(d);
+ sfree(d->bin);
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Current frame.
+ * \param[in] pbc PBC structure.
+ * \param data Should point to a \ref t_methoddata_insolidangle.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Creates a lookup structure that enables fast queries of whether a point
+ * is within the solid angle or not.
+ */
+static int
+init_frame_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
+{
+ t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
+ rvec dx;
+ int i;
+
+ free_surface_points(d);
+ clear_surface_points(d);
+ for (i = 0; i < d->span.nr; ++i)
+ {
+ if (pbc)
+ {
+ pbc_dx(pbc, d->span.x[i], d->center.x[0], dx);
+ }
+ else
+ {
+ rvec_sub(d->span.x[i], d->center.x[0], dx);
+ }
+ unitv(dx, dx);
+ store_surface_point(d, dx);
+ }
+ optimize_surface_points(d);
+ d->cfrac = -1;
+ return 0;
+}
+
+/*!
+ * \param[in] x Test point.
+ * \param[in] pbc PBC data (if NULL, no PBC are used).
+ * \param[in] data Pointer to a \c t_methoddata_insolidangle data structure.
+ * \returns TRUE if \p x is within the solid angle, FALSE otherwise.
+ */
+static gmx_bool
+accept_insolidangle(rvec x, t_pbc *pbc, void *data)
+{
+ t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
+ rvec dx;
+
+ if (pbc)
+ {
+ pbc_dx(pbc, x, d->center.x[0], dx);
+ }
+ else
+ {
+ rvec_sub(x, d->center.x[0], dx);
+ }
+ unitv(dx, dx);
+ return is_surface_covered(d, dx);
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_insolidangle.
+ *
+ * Calculates which atoms in \p g are within the solid angle spanned by
+ * \c t_methoddata_insolidangle::span and centered at
+ * \c t_methoddata_insolidangle::center, and stores the result in \p out->u.g.
+ */
+static int
+evaluate_insolidangle(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)data;
+ int b;
+
+ out->u.g->isize = 0;
+ for (b = 0; b < pos->nr; ++b)
+ {
+ if (accept_insolidangle(pos->x[b], pbc, data))
+ {
+ gmx_ana_pos_append(NULL, out->u.g, pos, b, 0);
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] sel Selection element to query.
+ * \returns TRUE if the covered fraction can be estimated for \p sel with
+ * _gmx_selelem_estimate_coverfrac(), FALSE otherwise.
+ */
+gmx_bool
+_gmx_selelem_can_estimate_cover(t_selelem *sel)
+{
+ t_selelem *child;
+ gmx_bool bFound;
+ gmx_bool bDynFound;
+
+ if (sel->type == SEL_BOOLEAN && sel->u.boolt == BOOL_OR)
+ {
+ return FALSE;
+ }
+ bFound = FALSE;
+ bDynFound = FALSE;
+ child = sel->child;
+ while (child)
+ {
+ if (child->type == SEL_EXPRESSION)
+ {
+ if (child->u.expr.method->name == sm_insolidangle.name)
+ {
+ if (bFound || bDynFound)
+ {
+ return FALSE;
+ }
+ bFound = TRUE;
+ }
+ else if (child->u.expr.method
+ && (child->u.expr.method->flags & SMETH_DYNAMIC))
+ {
+ if (bFound)
+ {
+ return FALSE;
+ }
+ bDynFound = TRUE;
+ }
+ }
+ else if (!_gmx_selelem_can_estimate_cover(child))
+ {
+ return FALSE;
+ }
+ child = child->next;
+ }
+ return TRUE;
+}
+
+/*!
+ * \param[in] sel Selection for which the fraction should be calculated.
+ * \returns Fraction of angles covered by the selection (between zero and one).
+ *
+ * The return value is undefined if _gmx_selelem_can_estimate_cover() returns
+ * FALSE.
+ * Should be called after gmx_ana_evaluate_selections() has been called for the
+ * frame.
+ */
+real
+_gmx_selelem_estimate_coverfrac(t_selelem *sel)
+{
+ t_selelem *child;
+ real cfrac;
+
+ if (sel->type == SEL_EXPRESSION && sel->u.expr.method->name == sm_insolidangle.name)
+ {
+ t_methoddata_insolidangle *d = (t_methoddata_insolidangle *)sel->u.expr.mdata;
+ if (d->cfrac < 0)
+ {
+ d->cfrac = estimate_covered_fraction(d);
+ }
+ return d->cfrac;
+ }
+ if (sel->type == SEL_BOOLEAN && sel->u.boolt == BOOL_NOT)
+ {
+ cfrac = _gmx_selelem_estimate_coverfrac(sel->child);
+ if (cfrac < 1.0)
+ {
+ return 1 - cfrac;
+ }
+ return 1;
+ }
+
+ /* Here, we assume that the selection is simple enough */
+ child = sel->child;
+ while (child)
+ {
+ cfrac = _gmx_selelem_estimate_coverfrac(child);
+ if (cfrac < 1.0)
+ {
+ return cfrac;
+ }
+ child = child->next;
+ }
+ return 1.0;
+}
+
+/*!
+ * \param[in] x1 Unit vector 1.
+ * \param[in] x2 Unit vector 2.
+ * \returns Minus the dot product of \p x1 and \p x2.
+ *
+ * This function is used internally to calculate the distance between the
+ * unit vectors \p x1 and \p x2 to find out whether \p x2 is within the
+ * cone centered at \p x1. Currently, the cosine of the angle is used
+ * for efficiency, and the minus is there to make it behave like a normal
+ * distance (larger values mean longer distances).
+ */
+static real
+sph_distc(rvec x1, rvec x2)
+{
+ return -iprod(x1, x2);
+}
+
+/*!
+ * \param[in] p Partition to search.
+ * \param[in] value Value to search for.
+ * \returns The partition index in \p p that contains \p value.
+ *
+ * If \p value is outside the range of \p p, the first/last index is returned.
+ * Otherwise, the return value \c i satisfies \c p->p[i].left<=value and
+ * \c p->p[i+1].left>value
+ */
+static int
+find_partition_bin(t_partition *p, real value)
+{
+ int pmin, pmax, pbin;
+
+ /* Binary search the partition */
+ pmin = 0; pmax = p->n;
+ while (pmax > pmin + 1)
+ {
+ pbin = pmin + (pmax - pmin) / 2;
+ if (p->p[pbin].left <= value)
+ {
+ pmin = pbin;
+ }
+ else
+ {
+ pmax = pbin;
+ }
+ }
+ pbin = pmin;
+ return pbin;
+}
+
+/*!
+ * \param[in] surf Surface data structure to search.
+ * \param[in] x Unit vector to find.
+ * \returns The bin index that contains \p x.
+ *
+ * The return value is an index to the \p surf->bin array.
+ */
+static int
+find_surface_bin(t_methoddata_insolidangle *surf, rvec x)
+{
+ real theta, phi;
+ int tbin, pbin;
+
+ theta = acos(x[ZZ]);
+ phi = atan2(x[YY], x[XX]);
+ tbin = floor(theta / surf->tbinsize);
+ if (tbin >= surf->ntbins)
+ {
+ tbin = surf->ntbins - 1;
+ }
+ pbin = find_partition_bin(&surf->tbin[tbin], phi);
+ return surf->tbin[tbin].p[pbin].bin;
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ *
+ * Clears the reference points from the bins and (re)initializes the edges
+ * of the azimuthal bins.
+ */
+static void
+clear_surface_points(t_methoddata_insolidangle *surf)
+{
+ int i, j, c;
+
+ surf->nbins = 0;
+ for (i = 0; i < surf->ntbins; ++i)
+ {
+ c = min(sin(surf->tbinsize*i), sin(surf->tbinsize*(i+1)))
+ * M_2PI / surf->targetbinsize + 1;
+ if (c <= 0)
+ {
+ c = 1;
+ }
+ surf->tbin[i].n = c;
+ for (j = 0; j < c; ++j)
+ {
+ surf->tbin[i].p[j].left = -M_PI + j*M_2PI/c - 0.0001;
+ surf->tbin[i].p[j].bin = surf->nbins;
+ surf->bin[surf->nbins].n = 0;
+ surf->nbins++;
+ }
+ surf->tbin[i].p[c].left = M_PI + 0.0001;
+ surf->tbin[i].p[c].bin = -1;
+ }
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ */
+static void
+free_surface_points(t_methoddata_insolidangle *surf)
+{
+ int i;
+
+ for (i = 0; i < surf->nbins; ++i)
+ {
+ if (surf->bin[i].x)
+ {
+ sfree(surf->bin[i].x);
+ }
+ surf->bin[i].n_alloc = 0;
+ surf->bin[i].x = NULL;
+ }
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ * \param[in] tbin Bin number in the zenith angle direction.
+ * \param[in] pbin Bin number in the azimuthal angle direction.
+ * \param[in] x Point to store.
+ */
+static void
+add_surface_point(t_methoddata_insolidangle *surf, int tbin, int pbin, rvec x)
+{
+ int bin;
+
+ bin = surf->tbin[tbin].p[pbin].bin;
+ /* Return if bin is already completely covered */
+ if (surf->bin[bin].n == -1)
+ return;
+ /* Allocate more space if necessary */
+ if (surf->bin[bin].n == surf->bin[bin].n_alloc) {
+ surf->bin[bin].n_alloc += 10;
+ srenew(surf->bin[bin].x, surf->bin[bin].n_alloc);
+ }
+ /* Add the point to the bin */
+ copy_rvec(x, surf->bin[bin].x[surf->bin[bin].n]);
+ ++surf->bin[bin].n;
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ * \param[in] tbin Bin number in the zenith angle direction.
+ * \param[in] pbin Bin number in the azimuthal angle direction.
+ */
+static void
+mark_surface_covered(t_methoddata_insolidangle *surf, int tbin, int pbin)
+{
+ int bin;
+
+ bin = surf->tbin[tbin].p[pbin].bin;
+ surf->bin[bin].n = -1;
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ * \param[in] tbin Bin number in the zenith angle direction.
+ * \param[in] phi Azimuthal angle of \p x.
+ * \param[in] pdelta1 Width of the cone at the lower edge of \p tbin.
+ * \param[in] pdelta2 Width of the cone at the uppper edge of \p tbin.
+ * \param[in] pdeltamax Max. width of the cone inside \p tbin.
+ * \param[in] x Point to store (should have unit length).
+ */
+static void
+update_surface_bin(t_methoddata_insolidangle *surf, int tbin,
+ real phi, real pdelta1, real pdelta2, real pdeltamax,
+ rvec x)
+{
+ real pdelta, phi1, phi2;
+ int pbin1, pbin2, pbin;
+
+ /* Find the edges of the bins affected */
+ pdelta = max(max(pdelta1, pdelta2), pdeltamax);
+ phi1 = phi - pdelta;
+ if (phi1 < -M_PI)
+ {
+ phi1 += M_2PI;
+ }
+ phi2 = phi + pdelta;
+ if (phi2 > M_PI)
+ {
+ phi2 -= M_2PI;
+ }
+ pbin1 = find_partition_bin(&surf->tbin[tbin], phi1);
+ pbin2 = find_partition_bin(&surf->tbin[tbin], phi2);
+ /* Find the edges of completely covered region */
+ pdelta = min(pdelta1, pdelta2);
+ phi1 = phi - pdelta;
+ if (phi1 < -M_PI)
+ {
+ phi1 += M_2PI;
+ }
+ phi2 = phi + pdelta;
+ /* Loop over all affected bins */
+ pbin = pbin1;
+ do
+ {
+ /* Wrap bin around if end reached */
+ if (pbin == surf->tbin[tbin].n)
+ {
+ pbin = 0;
+ phi1 -= M_2PI;
+ phi2 -= M_2PI;
+ }
+ /* Check if bin is completely covered and update */
+ if (surf->tbin[tbin].p[pbin].left >= phi1
+ && surf->tbin[tbin].p[pbin+1].left <= phi2)
+ {
+ mark_surface_covered(surf, tbin, pbin);
+ }
+ else
+ {
+ add_surface_point(surf, tbin, pbin, x);
+ }
+ }
+ while (pbin++ != pbin2); /* Loop including pbin2 */
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ * \param[in] x Point to store (should have unit length).
+ *
+ * Finds all the bins covered by the cone centered at \p x and calls
+ * update_surface_bin() to update them.
+ */
+static void
+store_surface_point(t_methoddata_insolidangle *surf, rvec x)
+{
+ real theta, phi;
+ real pdeltamax, tmax;
+ real theta1, theta2, pdelta1, pdelta2;
+ int tbin, pbin, bin;
+
+ theta = acos(x[ZZ]);
+ phi = atan2(x[YY], x[XX]);
+ /* Find the maximum extent in the phi direction */
+ if (theta <= surf->angcut)
+ {
+ pdeltamax = M_PI;
+ tmax = 0;
+ }
+ else if (theta >= M_PI - surf->angcut)
+ {
+ pdeltamax = M_PI;
+ tmax = M_PI;
+ }
+ else
+ {
+ pdeltamax = asin(sin(surf->angcut) / sin(theta));
+ tmax = acos(cos(theta) / cos(surf->angcut));
+ }
+ /* Find the first affected bin */
+ tbin = max(floor((theta - surf->angcut) / surf->tbinsize), 0);
+ theta1 = tbin * surf->tbinsize;
+ if (theta1 < theta - surf->angcut)
+ {
+ pdelta1 = 0;
+ }
+ else
+ {
+ pdelta1 = M_PI;
+ }
+ /* Loop through all affected bins */
+ while (tbin < ceil((theta + surf->angcut) / surf->tbinsize)
+ && tbin < surf->ntbins)
+ {
+ /* Calculate the next boundaries */
+ theta2 = (tbin+1) * surf->tbinsize;
+ if (theta2 > theta + surf->angcut)
+ {
+ pdelta2 = 0;
+ }
+ else if (tbin == surf->ntbins - 1)
+ {
+ pdelta2 = M_PI;
+ }
+ else
+ {
+ pdelta2 = 2*asin(sqrt(
+ (sqr(sin(surf->angcut/2)) - sqr(sin((theta2-theta)/2))) /
+ (sin(theta) * sin(theta2))));
+ }
+ /* Update the bin */
+ if (tmax >= theta1 && tmax <= theta2)
+ {
+ update_surface_bin(surf, tbin, phi, pdelta1, pdelta2, pdeltamax, x);
+ }
+ else
+ {
+ update_surface_bin(surf, tbin, phi, pdelta1, pdelta2, 0, x);
+ }
+ /* Next bin */
+ theta1 = theta2;
+ pdelta1 = pdelta2;
+ ++tbin;
+ }
+}
+
+/*!
+ * \param[in,out] surf Surface data structure.
+ *
+ * Currently, this function does nothing.
+ */
+static void
+optimize_surface_points(t_methoddata_insolidangle *surf)
+{
+ /* TODO: Implement */
+}
+
+/*!
+ * \param[in] surf Surface data structure.
+ * \returns An estimate for the area covered by the reference points.
+ */
+static real
+estimate_covered_fraction(t_methoddata_insolidangle *surf)
+{
+ int t, p, n;
+ real cfrac, tfrac, pfrac;
+
+ cfrac = 0.0;
+ for (t = 0; t < surf->ntbins; ++t)
+ {
+ tfrac = cos(t * surf->tbinsize) - cos((t+1) * surf->tbinsize);
+ for (p = 0; p < surf->tbin[t].n; ++p)
+ {
+ pfrac = surf->tbin[t].p[p+1].left - surf->tbin[t].p[p].left;
+ n = surf->bin[surf->tbin[t].p[p].bin].n;
+ if (n == -1) /* Bin completely covered */
+ {
+ cfrac += tfrac * pfrac;
+ }
+ else if (n > 0) /* Bin partially covered */
+ {
+ cfrac += tfrac * pfrac / 2; /* A rough estimate */
+ }
+ }
+ }
+ return cfrac / (4*M_PI);
+}
+
+/*!
+ * \param[in] surf Surface data structure to search.
+ * \param[in] x Unit vector to check.
+ * \returns TRUE if \p x is within the solid angle, FALSE otherwise.
+ */
+static gmx_bool
+is_surface_covered(t_methoddata_insolidangle *surf, rvec x)
+{
+ int bin, i;
+
+ bin = find_surface_bin(surf, x);
+ /* Check for completely covered bin */
+ if (surf->bin[bin].n == -1)
+ {
+ return TRUE;
+ }
+ /* Check each point that partially covers the bin */
+ for (i = 0; i < surf->bin[bin].n; ++i)
+ {
+ if (sph_distc(x, surf->bin[bin].x[i]) < surf->distccut)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements internal selection methods for numeric and string keyword
+ * evaluation.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h> /*old Mac needs types before regex.h*/
+#endif
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#define USE_REGEX
+#endif
+
+#include <macros.h>
+#include <smalloc.h>
+#include <string2.h>
+
+#include "gromacs/errorreporting/errorcontext.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "keywords.h"
+#include "parsetree.h"
+#include "scanner.h"
+#include "selelem.h"
+
+/** Allocates data for integer keyword evaluation. */
+static void *
+init_data_kwint(int npar, gmx_ana_selparam_t *param);
+/** Allocates data for real keyword evaluation. */
+static void *
+init_data_kwreal(int npar, gmx_ana_selparam_t *param);
+/** Allocates data for string keyword evaluation. */
+static void *
+init_data_kwstr(int npar, gmx_ana_selparam_t *param);
+/** Initializes data for integer keyword evaluation. */
+static int
+init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes data for real keyword evaluation. */
+static int
+init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes data for string keyword evaluation. */
+static int
+init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Frees the memory allocated for string keyword evaluation. */
+static void
+free_data_kwstr(void *data);
+/** Evaluates integer selection keywords. */
+static int
+evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates real selection keywords. */
+static int
+evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates string selection keywords. */
+static int
+evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+
+/*! \internal \brief
+ * Data structure for integer keyword expression evaluation.
+ */
+typedef struct t_methoddata_kwint
+{
+ /** Array of values for the keyword. */
+ int *v;
+ /** Number of ranges in the \p r array. */
+ int n;
+ /*! \brief
+ * Array of sorted integer ranges to match against.
+ *
+ * Each range is made of two integers, giving the endpoints (inclusive).
+ * This field stores the pointer to the ranges allocated by the
+ * parameter parser; see \ref SPAR_RANGES for more information.
+ */
+ int *r;
+} t_methoddata_kwint;
+
+/*! \internal \brief
+ * Data structure for real keyword expression evaluation.
+ */
+typedef struct t_methoddata_kwreal
+{
+ /** Array of values for the keyword. */
+ real *v;
+ /** Number of ranges in the \p r array. */
+ int n;
+ /*! \brief
+ * Array of sorted ranges to match against.
+ *
+ * Each range is made of two values, giving the endpoints (inclusive).
+ * This field stores the pointer to the ranges allocated by the
+ * parameter parser; see \ref SPAR_RANGES for more information.
+ */
+ real *r;
+} t_methoddata_kwreal;
+
+/*! \internal \brief
+ * Data structure for string keyword expression evaluation.
+ */
+typedef struct t_methoddata_kwstr
+{
+ /** Array of values for the keyword. */
+ char **v;
+ /** Number of elements in the \p val array. */
+ int n;
+ /*! \internal \brief
+ * Array of strings/regular expressions to match against.
+ */
+ struct t_methoddata_kwstr_match {
+ /** TRUE if the expression is a regular expression, FALSE otherwise. */
+ gmx_bool bRegExp;
+ /** The value to match against. */
+ union {
+#ifdef USE_REGEX
+ /** Compiled regular expression if \p bRegExp is TRUE. */
+ regex_t r;
+#endif
+ /** The string if \p bRegExp is FALSE; */
+ char *s;
+ } u;
+ } *m;
+} t_methoddata_kwstr;
+
+/** Parameters for integer keyword evaluation. */
+static gmx_ana_selparam_t smparams_keyword_int[] = {
+ {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
+ {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
+};
+
+/** Parameters for real keyword evaluation. */
+static gmx_ana_selparam_t smparams_keyword_real[] = {
+ {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL | SPAR_DYNAMIC},
+ {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
+};
+
+/** Parameters for string keyword evaluation. */
+static gmx_ana_selparam_t smparams_keyword_str[] = {
+ {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
+ {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
+};
+
+/** \internal Selection method data for integer keyword evaluation. */
+gmx_ana_selmethod_t sm_keyword_int = {
+ "kw_int", GROUP_VALUE, SMETH_SINGLEVAL,
+ asize(smparams_keyword_int), smparams_keyword_int,
+ &init_data_kwint,
+ NULL,
+ &init_kwint,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_keyword_int,
+ NULL,
+ {NULL, 0, NULL},
+};
+
+/** \internal Selection method data for real keyword evaluation. */
+gmx_ana_selmethod_t sm_keyword_real = {
+ "kw_real", GROUP_VALUE, SMETH_SINGLEVAL,
+ asize(smparams_keyword_real), smparams_keyword_real,
+ &init_data_kwreal,
+ NULL,
+ &init_kwreal,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_keyword_real,
+ NULL,
+ {NULL, 0, NULL},
+};
+
+/** \internal Selection method data for string keyword evaluation. */
+gmx_ana_selmethod_t sm_keyword_str = {
+ "kw_str", GROUP_VALUE, SMETH_SINGLEVAL,
+ asize(smparams_keyword_str), smparams_keyword_str,
+ &init_data_kwstr,
+ NULL,
+ &init_kwstr,
+ NULL,
+ &free_data_kwstr,
+ NULL,
+ &evaluate_keyword_str,
+ NULL,
+ {NULL, 0, NULL},
+};
+
+/** Initializes keyword evaluation for an arbitrary group. */
+static int
+init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes output for keyword evaluation in an arbitrary group. */
+static int
+init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data);
+/** Frees the data allocated for keyword evaluation in an arbitrary group. */
+static void
+free_data_kweval(void *data);
+/** Initializes frame evaluation for keyword evaluation in an arbitrary group. */
+static int
+init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
+/** Evaluates keywords in an arbitrary group. */
+static int
+evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+
+/*! \internal \brief
+ * Data structure for keyword evaluation in arbitrary groups.
+ */
+typedef struct
+{
+ /** Wrapped keyword method for evaluating the values. */
+ gmx_ana_selmethod_t *kwmethod;
+ /** Method data for \p kwmethod. */
+ void *kwmdata;
+ /** Group in which \p kwmethod should be evaluated. */
+ gmx_ana_index_t g;
+} t_methoddata_kweval;
+
+/** Parameters for keyword evaluation in an arbitrary group. */
+static gmx_ana_selparam_t smparams_kweval[] = {
+ {NULL, {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
+};
+
+
+/********************************************************************
+ * INTEGER KEYWORD EVALUATION
+ ********************************************************************/
+
+/*!
+ * \param[in] npar Not used.
+ * \param param Not used.
+ * \returns Pointer to the allocated data (\ref t_methoddata_kwint).
+ *
+ * Allocates memory for a \ref t_methoddata_kwint structure.
+ */
+static void *
+init_data_kwint(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_kwint *data;
+
+ snew(data, 1);
+ return data;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used (should be 2).
+ * \param[in] param Method parameters (should point to \ref smparams_keyword_int).
+ * \param[in] data Should point to \ref t_methoddata_kwint.
+ * \returns 0 (the initialization always succeeds).
+ */
+static int
+init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_kwint *d = (t_methoddata_kwint *)data;
+
+ d->v = param[0].val.u.i;
+ d->n = param[1].val.nr;
+ d->r = param[1].val.u.i;
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_kwint.
+ *
+ * Does a binary search to find which atoms match the ranges in the
+ * \c t_methoddata_kwint structure for this selection.
+ * Matching atoms are stored in \p out->u.g.
+ */
+static int
+evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_kwint *d = (t_methoddata_kwint *)data;
+ int n, i, j, jmin, jmax;
+ int val;
+
+ out->u.g->isize = 0;
+ n = d->n;
+ for (i = 0; i < g->isize; ++i)
+ {
+ val = d->v[i];
+ if (d->r[0] > val || d->r[2*n-1] < val)
+ {
+ continue;
+ }
+ jmin = 0;
+ jmax = n;
+ while (jmax - jmin > 1)
+ {
+ j = jmin + (jmax - jmin) / 2;
+ if (val < d->r[2*j])
+ {
+ jmax = j;
+ }
+ else
+ {
+ jmin = j;
+ if (val <= d->r[2*j+1])
+ {
+ break;
+ }
+ /* ++jmin;*/
+ }
+ }
+ if (val <= d->r[2*jmin+1])
+ {
+ out->u.g->index[out->u.g->isize++] = g->index[i];
+ }
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * REAL KEYWORD EVALUATION
+ ********************************************************************/
+
+/*!
+ * \param[in] npar Not used.
+ * \param param Not used.
+ * \returns Pointer to the allocated data (\ref t_methoddata_kwreal).
+ *
+ * Allocates memory for a \ref t_methoddata_kwreal structure.
+ */
+static void *
+init_data_kwreal(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_kwreal *data;
+
+ snew(data, 1);
+ return data;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used (should be 2).
+ * \param[in] param Method parameters (should point to \ref smparams_keyword_real).
+ * \param[in] data Should point to \ref t_methoddata_kwreal.
+ * \returns 0 (the initialization always succeeds).
+ */
+static int
+init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
+
+ d->v = param[0].val.u.r;
+ d->n = param[1].val.nr;
+ d->r = param[1].val.u.r;
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_kwreal.
+ *
+ * Does a binary search to find which atoms match the ranges in the
+ * \c t_methoddata_kwreal structure for this selection.
+ * Matching atoms are stored in \p out->u.g.
+ */
+static int
+evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
+ int n, i, j, jmin, jmax;
+ real val;
+
+ out->u.g->isize = 0;
+ n = d->n;
+ for (i = 0; i < g->isize; ++i)
+ {
+ val = d->v[i];
+ if (d->r[0] > val || d->r[2*n-1] < val)
+ {
+ continue;
+ }
+ jmin = 0;
+ jmax = n;
+ while (jmax - jmin > 1)
+ {
+ j = jmin + (jmax - jmin) / 2;
+ if (val < d->r[2*j])
+ {
+ jmax = j;
+ }
+ else
+ {
+ jmin = j;
+ if (val <= d->r[2*j+1])
+ {
+ break;
+ }
+ /* ++jmin;*/
+ }
+ }
+ if (val <= d->r[2*jmin+1])
+ {
+ out->u.g->index[out->u.g->isize++] = g->index[i];
+ }
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * STRING KEYWORD EVALUATION
+ ********************************************************************/
+
+/*!
+ * \param[in] npar Not used.
+ * \param param Not used.
+ * \returns Pointer to the allocated data (\ref t_methoddata_kwstr).
+ *
+ * Allocates memory for a \ref t_methoddata_kwstr structure.
+ */
+static void *
+init_data_kwstr(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_kwstr *data;
+
+ snew(data, 1);
+ return data;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used (should be 2).
+ * \param[in] param Method parameters (should point to \ref smparams_keyword_str).
+ * \param[in] data Should point to \ref t_methoddata_kwstr.
+ * \returns 0 (the initialization always succeeds).
+ */
+static int
+init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
+ char *buf;
+ char *s;
+ int i;
+ size_t j;
+ gmx_bool bRegExp;
+
+ d->v = param[0].val.u.s;
+ d->n = param[1].val.nr;
+ /* Return if this is not the first time */
+ if (d->m)
+ {
+ return 0;
+ }
+ snew(d->m, d->n);
+ for (i = 0; i < d->n; ++i)
+ {
+ s = param[1].val.u.s[i];
+ bRegExp = FALSE;
+ for (j = 0; j < strlen(s); ++j)
+ {
+ if (ispunct(s[j]) && s[j] != '?' && s[j] != '*')
+ {
+ bRegExp = TRUE;
+ break;
+ }
+ }
+ if (bRegExp)
+ {
+#ifdef USE_REGEX
+ snew(buf, strlen(s) + 3);
+ sprintf(buf, "^%s$", s);
+ if (regcomp(&d->m[i].u.r, buf, REG_EXTENDED | REG_NOSUB))
+ {
+ bRegExp = FALSE;
+ fprintf(stderr, "WARNING: error in regular expression,\n"
+ " will match '%s' as a simple string\n", s);
+ }
+ sfree(buf);
+#else
+ bRegExp = FALSE;
+ fprintf(stderr, "WARNING: no regular expressions support,\n"
+ " will match '%s' as a simple string\n", s);
+#endif
+ }
+ if (!bRegExp)
+ {
+ d->m[i].u.s = s;
+ }
+ d->m[i].bRegExp = bRegExp;
+ }
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \ref t_methoddata_kwstr).
+ *
+ * Frees the memory allocated for t_methoddata_kwstr::val.
+ */
+static void
+free_data_kwstr(void *data)
+{
+ t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
+ int i;
+
+ for (i = 0; i < d->n; ++i)
+ {
+ if (d->m[i].bRegExp)
+ {
+#ifdef USE_REGEX
+ /* This branch should only be taken if regular expressions
+ * are available, but the ifdef is still needed. */
+ regfree(&d->m[i].u.r);
+#endif
+ }
+ }
+ sfree(d->m);
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_kwstr.
+ *
+ * Does a linear search to find which atoms match the strings in the
+ * \c t_methoddata_kwstr structure for this selection.
+ * Wildcards are allowed in the strings.
+ * Matching atoms are stored in \p out->u.g.
+ */
+static int
+evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
+ int i, j;
+ gmx_bool bFound;
+
+ out->u.g->isize = 0;
+ for (i = 0; i < g->isize; ++i)
+ {
+ bFound = FALSE;
+ for (j = 0; j < d->n && !bFound; ++j)
+ {
+ if (d->m[j].bRegExp)
+ {
+#ifdef USE_REGEX
+ /* This branch should only be taken if regular expressions
+ * are available, but the ifdef is still needed. */
+ if (!regexec(&d->m[j].u.r, d->v[i], 0, NULL, 0))
+ {
+ bFound = TRUE;
+ }
+#endif
+ }
+ else
+ {
+ if (gmx_wcmatch(d->m[j].u.s, d->v[i]) == 0)
+ {
+ bFound = TRUE;
+ }
+ }
+ }
+ if (bFound)
+ {
+ out->u.g->index[out->u.g->isize++] = g->index[i];
+ }
+ }
+ return 0;
+}
+
+
+/********************************************************************
+ * KEYWORD EVALUATION FOR ARBITRARY GROUPS
+ ********************************************************************/
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used.
+ * \param[in] param Not used.
+ * \param[in] data Should point to \ref t_methoddata_kweval.
+ * \returns 0 on success, a non-zero error code on return.
+ *
+ * Calls the initialization method of the wrapped keyword.
+ */
+static int
+init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_kweval *d = (t_methoddata_kweval *)data;
+
+ return d->kwmethod->init(top, 0, NULL, d->kwmdata);
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in,out] out Pointer to output data structure.
+ * \param[in,out] data Should point to \c t_methoddata_kweval.
+ * \returns 0 for success.
+ */
+static int
+init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_kweval *d = (t_methoddata_kweval *)data;
+
+ out->nr = d->g.isize;
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \c t_methoddata_kweval).
+ *
+ * Frees the memory allocated for all the members of \c t_methoddata_kweval.
+ */
+static void
+free_data_kweval(void *data)
+{
+ t_methoddata_kweval *d = (t_methoddata_kweval *)data;
+
+ _gmx_selelem_free_method(d->kwmethod, d->kwmdata);
+}
+
+/*!
+ * \param[in] top Topology.
+ * \param[in] fr Current frame.
+ * \param[in] pbc PBC structure.
+ * \param data Should point to a \ref t_methoddata_kweval.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Creates a lookup structure that enables fast queries of whether a point
+ * is within the solid angle or not.
+ */
+static int
+init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
+{
+ t_methoddata_kweval *d = (t_methoddata_kweval *)data;
+
+ return d->kwmethod->init_frame(top, fr, pbc, d->kwmdata);
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_kweval.
+ *
+ * Calls the evaluation function of the wrapped keyword with the given
+ * parameters, with the exception of using \c t_methoddata_kweval::g for the
+ * evaluation group.
+ */
+static int
+evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_kweval *d = (t_methoddata_kweval *)data;
+
+ return d->kwmethod->update(top, fr, pbc, &d->g, out, d->kwmdata);
+}
+
+/*!
+ * \param[out] selp Pointer to receive a pointer to the created selection
+ * element (set to NULL on error).
+ * \param[in] method Keyword selection method to evaluate.
+ * \param[in] param Parameter that gives the group to evaluate \p method in.
+ * \param[in] scanner Scanner data structure.
+ * \returns 0 on success, non-zero error code on error.
+ *
+ * Creates a \ref SEL_EXPRESSION selection element (pointer put in \c *selp)
+ * that evaluates the keyword method given by \p method in the group given by
+ * \p param.
+ */
+int
+_gmx_sel_init_keyword_evaluator(t_selelem **selp, gmx_ana_selmethod_t *method,
+ t_selexpr_param *param, void *scanner)
+{
+ t_selelem *sel;
+ t_methoddata_kweval *data;
+
+ gmx::AbstractErrorReporter *errors = _gmx_sel_lexer_error_reporter(scanner);
+ char buf[1024];
+ sprintf(buf, "In evaluation of '%s'", method->name);
+ gmx::ErrorContext context(errors, buf);
+
+ if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
+ || method->outinit || method->pupdate)
+ {
+ _gmx_selexpr_free_params(param);
+ GMX_ERROR(gmx::eeInternalError,
+ "Unsupported keyword method for arbitrary group evaluation");
+ }
+
+ *selp = NULL;
+ sel = _gmx_selelem_create(SEL_EXPRESSION);
+ _gmx_selelem_set_method(sel, method, scanner);
+
+ snew(data, 1);
+ data->kwmethod = sel->u.expr.method;
+ data->kwmdata = sel->u.expr.mdata;
+ gmx_ana_index_clear(&data->g);
+
+ snew(sel->u.expr.method, 1);
+ memcpy(sel->u.expr.method, data->kwmethod, sizeof(gmx_ana_selmethod_t));
+ sel->u.expr.method->flags |= SMETH_VARNUMVAL;
+ sel->u.expr.method->init_data = NULL;
+ sel->u.expr.method->set_poscoll = NULL;
+ sel->u.expr.method->init = method->init ? &init_kweval : NULL;
+ sel->u.expr.method->outinit = &init_output_kweval;
+ sel->u.expr.method->free = &free_data_kweval;
+ sel->u.expr.method->init_frame = method->init_frame ? &init_frame_kweval : NULL;
+ sel->u.expr.method->update = &evaluate_kweval;
+ sel->u.expr.method->pupdate = NULL;
+ sel->u.expr.method->nparams = asize(smparams_kweval);
+ sel->u.expr.method->param = smparams_kweval;
+ _gmx_selelem_init_method_params(sel, scanner);
+ sel->u.expr.mdata = data;
+
+ sel->u.expr.method->param[0].val.u.g = &data->g;
+
+ sfree(param->name);
+ param->name = NULL;
+ if (!_gmx_sel_parse_params(param, sel->u.expr.method->nparams,
+ sel->u.expr.method->param, sel, scanner))
+ {
+ _gmx_selelem_free(sel);
+ return -1;
+ }
+ *selp = sel;
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements the \p merge and \p plus selection modifiers.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <macros.h>
+#include <smalloc.h>
+#include <vec.h>
+
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+
+/*! \internal \brief
+ * Data structure for the merging selection modifiers.
+ */
+typedef struct
+{
+ /** Input positions. */
+ gmx_ana_pos_t p1;
+ /** Other input positions. */
+ gmx_ana_pos_t p2;
+ /** Group to store the output atom indices. */
+ gmx_ana_index_t g;
+ /** Stride for merging (\c stride values from \c p1 for each in \c p2). */
+ int stride;
+} t_methoddata_merge;
+
+/** Allocates data for the merging selection modifiers. */
+static void *
+init_data_merge(int npar, gmx_ana_selparam_t *param);
+/** Initializes data for the merging selection modifiers. */
+static int
+init_merge(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes output for the \p merge selection modifier. */
+static int
+init_output_merge(t_topology *top, gmx_ana_selvalue_t *out, void *data);
+/** Initializes output for the \p plus selection modifier. */
+static int
+init_output_plus(t_topology *top, gmx_ana_selvalue_t *out, void *data);
+/** Frees the memory allocated for the merging selection modifiers. */
+static void
+free_data_merge(void *data);
+/** Evaluates the \p merge selection modifier. */
+static int
+evaluate_merge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p plus selection modifier. */
+static int
+evaluate_plus(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data);
+
+/** Parameters for the merging selection modifiers. */
+static gmx_ana_selparam_t smparams_merge[] = {
+ {NULL, {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+ {NULL, {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+ {"stride", {INT_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
+};
+
+/** Help text for the merging selection modifiers. */
+static const char *help_merge[] = {
+ "MERGING SELECTIONS[PAR]",
+
+ "[TT]POSEXPR merge POSEXPR [stride INT][tt][BR]",
+ "[TT]POSEXPR merge POSEXPR [merge POSEXPR ...][tt][BR]",
+ "[TT]POSEXPR plus POSEXPR [plus POSEXPR ...][tt][PAR]",
+
+ "Basic selection keywords can only create selections where each atom",
+ "occurs at most once. The [TT]merge[tt] and [TT]plus[tt] selection",
+ "keywords can be used to work around this limitation. Both create",
+ "a selection that contains the positions from all the given position",
+ "expressions, even if they contain duplicates.",
+ "The difference between the two is that [TT]merge[tt] expects two or more",
+ "selections with the same number of positions, and the output contains",
+ "the input positions selected from each expression in turn, i.e.,",
+ "the output is like A1 B1 A2 B2 and so on. It is also possible to merge",
+ "selections of unequal size as long as the size of the first is a",
+ "multiple of the second one. The [TT]stride[tt] parameter can be used",
+ "to explicitly provide this multiplicity.",
+ "[TT]plus[tt] simply concatenates the positions after each other, and",
+ "can work also with selections of different sizes.",
+ "These keywords are valid only at the selection level, not in any",
+ "subexpressions.[PAR]",
+};
+
+/** \internal Selection method data for the \p plus modifier. */
+gmx_ana_selmethod_t sm_merge = {
+ "merge", POS_VALUE, SMETH_MODIFIER,
+ asize(smparams_merge), smparams_merge,
+ &init_data_merge,
+ NULL,
+ &init_merge,
+ &init_output_merge,
+ &free_data_merge,
+ NULL,
+ NULL,
+ &evaluate_merge,
+ {"merge POSEXPR", asize(help_merge), help_merge},
+};
+
+/** \internal Selection method data for the \p plus modifier. */
+gmx_ana_selmethod_t sm_plus = {
+ "plus", POS_VALUE, SMETH_MODIFIER,
+ asize(smparams_merge)-1, smparams_merge,
+ &init_data_merge,
+ NULL,
+ &init_merge,
+ &init_output_plus,
+ &free_data_merge,
+ NULL,
+ NULL,
+ &evaluate_plus,
+ {"plus POSEXPR", asize(help_merge), help_merge},
+};
+
+/*!
+ * \param[in] npar Should be 2 for \c plus and 3 for \c merge.
+ * \param[in,out] param Method parameters (should point to a copy of
+ * \ref smparams_merge).
+ * \returns Pointer to the allocated data (\p t_methoddata_merge).
+ *
+ * Allocates memory for a \p t_methoddata_merge structure.
+ */
+static void *
+init_data_merge(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_merge *data;
+
+ snew(data, 1);
+ data->stride = 0;
+ param[0].val.u.p = &data->p1;
+ param[1].val.u.p = &data->p2;
+ if (npar > 2)
+ {
+ param[2].val.u.i = &data->stride;
+ }
+ return data;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used (should be 2 or 3).
+ * \param[in] param Method parameters (should point to \ref smparams_merge).
+ * \param[in] data Should point to a \p t_methoddata_merge.
+ * \returns 0 if everything is successful, -1 on error.
+ */
+static int
+init_merge(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+ int i;
+
+ if (d->stride < 0)
+ {
+ fprintf(stderr, "error: stride for merging should be positive\n");
+ return -1;
+ }
+ /* If no stride given, deduce it from the input sizes */
+ if (d->stride == 0)
+ {
+ d->stride = d->p1.nr / d->p2.nr;
+ }
+ if (d->p1.nr != d->stride*d->p2.nr)
+ {
+ fprintf(stderr, "error: the number of positions to be merged are not compatible\n");
+ return -1;
+ }
+ /* We access the m.b.nra field instead of g->isize in the position
+ * data structures to handle cases where g is NULL
+ * (this occurs with constant positions. */
+ gmx_ana_index_reserve(&d->g, d->p1.m.b.nra + d->p2.m.b.nra);
+ d->g.isize = d->p1.m.b.nra + d->p2.m.b.nra;
+ return 0;
+}
+
+/*! \brief
+ * Does common initialization to all merging modifiers.
+ *
+ * \param[in] top Topology data structure.
+ * \param[in,out] out Pointer to output data structure.
+ * \param[in,out] data Should point to \c t_methoddata_merge.
+ * \returns 0 for success.
+ */
+static int
+init_output_common(t_topology *top, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+
+ if (d->p1.m.type != d->p2.m.type)
+ {
+ /* TODO: Maybe we could pick something else here? */
+ out->u.p->m.type = INDEX_UNKNOWN;
+ }
+ else
+ {
+ out->u.p->m.type = d->p1.m.type;
+ }
+ gmx_ana_pos_reserve(out->u.p, d->p1.nr + d->p2.nr, d->g.isize);
+ if (d->p1.v)
+ {
+ gmx_ana_pos_reserve_velocities(out->u.p);
+ }
+ if (d->p1.f)
+ {
+ gmx_ana_pos_reserve_forces(out->u.p);
+ }
+ gmx_ana_pos_set_evalgrp(out->u.p, &d->g);
+ gmx_ana_pos_empty_init(out->u.p);
+ d->g.isize = 0;
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology data structure.
+ * \param[in,out] out Pointer to output data structure.
+ * \param[in,out] data Should point to \c t_methoddata_merge.
+ * \returns 0 for success.
+ */
+static int
+init_output_merge(t_topology *top, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+ int i, j;
+
+ init_output_common(top, out, data);
+ for (i = 0; i < d->p2.nr; ++i)
+ {
+ for (j = 0; j < d->stride; ++j)
+ {
+ gmx_ana_pos_append_init(out->u.p, &d->g, &d->p1, d->stride*i+j);
+ }
+ gmx_ana_pos_append_init(out->u.p, &d->g, &d->p2, i);
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology data structure.
+ * \param[in,out] out Pointer to output data structure.
+ * \param[in,out] data Should point to \c t_methoddata_merge.
+ * \returns 0 for success.
+ */
+static int
+init_output_plus(t_topology *top, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+ int i;
+
+ init_output_common(top, out, data);
+ for (i = 0; i < d->p1.nr; ++i)
+ {
+ gmx_ana_pos_append_init(out->u.p, &d->g, &d->p1, i);
+ }
+ for (i = 0; i < d->p2.nr; ++i)
+ {
+ gmx_ana_pos_append_init(out->u.p, &d->g, &d->p2, i);
+ }
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \p t_methoddata_merge).
+ *
+ * Frees the memory allocated for \c t_methoddata_merge.
+ */
+static void
+free_data_merge(void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+
+ gmx_ana_index_deinit(&d->g);
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Not used.
+ * \param[in] pbc Not used.
+ * \param[in] p Positions to merge (should point to \p data->p1).
+ * \param[out] out Output data structure (\p out->u.p is used).
+ * \param[in] data Should point to a \p t_methoddata_merge.
+ * \returns 0 on success.
+ */
+static int
+evaluate_merge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+ int i, j;
+ int refid;
+
+ if (d->p1.nr != d->stride*d->p2.nr)
+ {
+ fprintf(stderr, "error: the number of positions to be merged are not compatible\n");
+ return -1;
+ }
+ d->g.isize = 0;
+ gmx_ana_pos_empty(out->u.p);
+ for (i = 0; i < d->p2.nr; ++i)
+ {
+ for (j = 0; j < d->stride; ++j)
+ {
+ refid = d->p1.m.refid[d->stride*i+j];
+ if (refid != -1)
+ {
+ refid = (d->stride+1) * (refid / d->stride) + (refid % d->stride);
+ }
+ gmx_ana_pos_append(out->u.p, &d->g, &d->p1, d->stride*i+j, refid);
+ }
+ refid = (d->stride+1)*d->p2.m.refid[i]+d->stride;
+ gmx_ana_pos_append(out->u.p, &d->g, &d->p2, i, refid);
+ }
+ gmx_ana_pos_append_finish(out->u.p);
+ return 0;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Not used.
+ * \param[in] pbc Not used.
+ * \param[in] p Positions to merge (should point to \p data->p1).
+ * \param[out] out Output data structure (\p out->u.p is used).
+ * \param[in] data Should point to a \p t_methoddata_merge.
+ * \returns 0 on success.
+ */
+static int
+evaluate_plus(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_merge *d = (t_methoddata_merge *)data;
+ int i;
+ int refid;
+
+ d->g.isize = 0;
+ gmx_ana_pos_empty(out->u.p);
+ for (i = 0; i < d->p1.nr; ++i)
+ {
+ refid = d->p1.m.refid[i];
+ gmx_ana_pos_append(out->u.p, &d->g, &d->p1, i, refid);
+ }
+ for (i = 0; i < d->p2.nr; ++i)
+ {
+ refid = d->p2.m.refid[i];
+ if (refid != -1)
+ {
+ refid += d->p1.m.b.nr;
+ }
+ gmx_ana_pos_append(out->u.p, &d->g, &d->p2, i, refid);
+ }
+ gmx_ana_pos_append_finish(out->u.p);
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements the \p permute selection modifier.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <macros.h>
+#include <smalloc.h>
+#include <vec.h>
+
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+
+/*! \internal \brief
+ * Data structure for the \p permute selection modifier.
+ */
+typedef struct
+{
+ /** Positions to permute. */
+ gmx_ana_pos_t p;
+ /** Group to receive the output permutation. */
+ gmx_ana_index_t g;
+ /** Number of elements in the permutation. */
+ int n;
+ /** Array describing the permutation. */
+ int *perm;
+ /** Array that has the permutation reversed. */
+ int *rperm;
+} t_methoddata_permute;
+
+/** Allocates data for the \p permute selection modifier. */
+static void *
+init_data_permute(int npar, gmx_ana_selparam_t *param);
+/** Initializes data for the \p permute selection modifier. */
+static int
+init_permute(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes output for the \p permute selection modifier. */
+static int
+init_output_permute(t_topology *top, gmx_ana_selvalue_t *out, void *data);
+/** Frees the memory allocated for the \p permute selection modifier. */
+static void
+free_data_permute(void *data);
+/** Evaluates the \p permute selection modifier. */
+static int
+evaluate_permute(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data);
+
+/** Parameters for the \p permute selection modifier. */
+static gmx_ana_selparam_t smparams_permute[] = {
+ {NULL, {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+ {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
+};
+
+/** Help text for the \p permute selection modifier. */
+static const char *help_permute[] = {
+ "PERMUTING SELECTIONS[PAR]",
+
+ "[TT]permute P1 ... PN[tt][PAR]",
+
+ "By default, all selections are evaluated such that the atom indices are",
+ "returned in ascending order. This can be changed by appending",
+ "[TT]permute P1 P2 ... PN[tt] to an expression.",
+ "The [TT]Pi[tt] should form a permutation of the numbers 1 to N.",
+ "This keyword permutes each N-position block in the selection such that",
+ "the i'th position in the block becomes Pi'th.",
+ "Note that it is the positions that are permuted, not individual atoms.",
+ "A fatal error occurs if the size of the selection is not a multiple of n.",
+ "It is only possible to permute the whole selection expression, not any",
+ "subexpressions, i.e., the [TT]permute[tt] keyword should appear last in",
+ "a selection.",
+};
+
+/** \internal Selection method data for the \p permute modifier. */
+gmx_ana_selmethod_t sm_permute = {
+ "permute", POS_VALUE, SMETH_MODIFIER,
+ asize(smparams_permute), smparams_permute,
+ &init_data_permute,
+ NULL,
+ &init_permute,
+ &init_output_permute,
+ &free_data_permute,
+ NULL,
+ NULL,
+ &evaluate_permute,
+ {"permute P1 ... PN", asize(help_permute), help_permute},
+};
+
+/*!
+ * \param[in] npar Not used (should be 2).
+ * \param[in,out] param Method parameters (should point to a copy of
+ * \ref smparams_permute).
+ * \returns Pointer to the allocated data (\p t_methoddata_permute).
+ *
+ * Allocates memory for a \p t_methoddata_permute structure.
+ */
+static void *
+init_data_permute(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_permute *data;
+
+ snew(data, 1);
+ param[0].val.u.p = &data->p;
+ return data;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used (should be 2).
+ * \param[in] param Method parameters (should point to \ref smparams_permute).
+ * \param[in] data Should point to a \p t_methoddata_permute.
+ * \returns 0 if the input permutation is valid, -1 on error.
+ */
+static int
+init_permute(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_permute *d = (t_methoddata_permute *)data;
+ int i;
+
+ gmx_ana_index_reserve(&d->g, d->p.g->isize);
+ d->n = param[1].val.nr;
+ d->perm = param[1].val.u.i;
+ if (d->p.nr % d->n != 0)
+ {
+ fprintf(stderr, "error: the number of positions to be permuted is not divisible by %d\n",
+ d->n);
+ return -1;
+ }
+ snew(d->rperm, d->n);
+ for (i = 0; i < d->n; ++i)
+ {
+ d->rperm[i] = -1;
+ }
+ for (i = 0; i < d->n; ++i)
+ {
+ d->perm[i]--;
+ if (d->perm[i] < 0 || d->perm[i] >= d->n)
+ {
+ fprintf(stderr, "invalid permutation");
+ return -1;
+ }
+ if (d->rperm[d->perm[i]] >= 0)
+ {
+ fprintf(stderr, "invalid permutation");
+ return -1;
+ }
+ d->rperm[d->perm[i]] = i;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology data structure.
+ * \param[in,out] out Pointer to output data structure.
+ * \param[in,out] data Should point to \c t_methoddata_permute.
+ * \returns 0 for success.
+ */
+static int
+init_output_permute(t_topology *top, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_permute *d = (t_methoddata_permute *)data;
+ int i, j, b, k;
+
+ gmx_ana_pos_copy(out->u.p, &d->p, TRUE);
+ gmx_ana_pos_set_evalgrp(out->u.p, &d->g);
+ d->g.isize = 0;
+ gmx_ana_pos_empty_init(out->u.p);
+ for (i = 0; i < d->p.nr; i += d->n)
+ {
+ for (j = 0; j < d->n; ++j)
+ {
+ b = i + d->rperm[j];
+ gmx_ana_pos_append_init(out->u.p, &d->g, &d->p, b);
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \p t_methoddata_permute).
+ *
+ * Frees the memory allocated for \c t_methoddata_permute.
+ */
+static void
+free_data_permute(void *data)
+{
+ t_methoddata_permute *d = (t_methoddata_permute *)data;
+
+ gmx_ana_index_deinit(&d->g);
+ sfree(d->rperm);
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Not used.
+ * \param[in] pbc Not used.
+ * \param[in] p Positions to permute (should point to \p data->p).
+ * \param[out] out Output data structure (\p out->u.p is used).
+ * \param[in] data Should point to a \p t_methoddata_permute.
+ * \returns 0 if \p p could be permuted, -1 on error.
+ *
+ * Returns -1 if the size of \p p is not divisible by the number of
+ * elements in the permutation.
+ */
+static int
+evaluate_permute(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *p, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_permute *d = (t_methoddata_permute *)data;
+ int i, j, b, k;
+ int refid;
+
+ if (d->p.nr % d->n != 0)
+ {
+ fprintf(stderr, "error: the number of positions to be permuted is not divisible by %d\n",
+ d->n);
+ return -1;
+ }
+ d->g.isize = 0;
+ gmx_ana_pos_empty(out->u.p);
+ for (i = 0; i < d->p.nr; i += d->n)
+ {
+ for (j = 0; j < d->n; ++j)
+ {
+ b = i + d->rperm[j];
+ refid = d->p.m.refid[b];
+ if (refid != -1)
+ {
+ /* De-permute the reference ID */
+ refid = refid - (refid % d->n) + d->perm[refid % d->n];
+ }
+ gmx_ana_pos_append(out->u.p, &d->g, p, b, refid);
+ }
+ }
+ gmx_ana_pos_append_finish(out->u.p);
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements position evaluation selection methods.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <macros.h>
+#include <smalloc.h>
+#include <string2.h>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+
+#include "keywords.h"
+#include "selelem.h"
+
+/*! \internal \brief
+ * Data structure for position keyword evaluation.
+ */
+typedef struct
+{
+ /** Position calculation collection to use. */
+ gmx_ana_poscalc_coll_t *pcc;
+ /** Index group for which the center should be evaluated. */
+ gmx_ana_index_t g;
+ /** Position evaluation data structure. */
+ gmx_ana_poscalc_t *pc;
+ /** TRUE if periodic boundary conditions should be used. */
+ gmx_bool bPBC;
+ /** Type of positions to calculate. */
+ char *type;
+ /** Flags for the position calculation. */
+ int flags;
+} t_methoddata_pos;
+
+/** Allocates data for position evaluation selection methods. */
+static void *
+init_data_pos(int npar, gmx_ana_selparam_t *param);
+/** Sets the position calculation collection for position evaluation selection methods. */
+static void
+set_poscoll_pos(gmx_ana_poscalc_coll_t *pcc, void *data);
+/** Initializes position evaluation keywords. */
+static int
+init_kwpos(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes the \p cog selection method. */
+static int
+init_cog(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes the \p cog selection method. */
+static int
+init_com(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Initializes output for position evaluation selection methods. */
+static int
+init_output_pos(t_topology *top, gmx_ana_selvalue_t *out, void *data);
+/** Frees the data allocated for position evaluation selection methods. */
+static void
+free_data_pos(void *data);
+/** Evaluates position evaluation selection methods. */
+static int
+evaluate_pos(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+
+/** Parameters for position keyword evaluation. */
+static gmx_ana_selparam_t smparams_keyword_pos[] = {
+ {NULL, {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
+};
+
+/** Parameters for the \p cog and \p com selection methods. */
+static gmx_ana_selparam_t smparams_com[] = {
+ {"of", {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
+ {"pbc", {NO_VALUE, 0, {NULL}}, NULL, 0},
+};
+
+/** \internal Selection method data for position keyword evaluation. */
+gmx_ana_selmethod_t sm_keyword_pos = {
+ "kw_pos", POS_VALUE, SMETH_DYNAMIC | SMETH_VARNUMVAL,
+ asize(smparams_keyword_pos), smparams_keyword_pos,
+ &init_data_pos,
+ &set_poscoll_pos,
+ &init_kwpos,
+ &init_output_pos,
+ &free_data_pos,
+ NULL,
+ &evaluate_pos,
+ NULL,
+ {NULL, 0, NULL},
+};
+
+/** \internal Selection method data for the \p cog method. */
+gmx_ana_selmethod_t sm_cog = {
+ "cog", POS_VALUE, SMETH_DYNAMIC | SMETH_SINGLEVAL,
+ asize(smparams_com), smparams_com,
+ &init_data_pos,
+ &set_poscoll_pos,
+ &init_cog,
+ &init_output_pos,
+ &free_data_pos,
+ NULL,
+ &evaluate_pos,
+ NULL,
+ {"cog of ATOM_EXPR [pbc]", 0, NULL},
+};
+
+/** \internal Selection method data for the \p com method. */
+gmx_ana_selmethod_t sm_com = {
+ "com", POS_VALUE, SMETH_REQTOP | SMETH_DYNAMIC | SMETH_SINGLEVAL,
+ asize(smparams_com), smparams_com,
+ &init_data_pos,
+ &set_poscoll_pos,
+ &init_com,
+ &init_output_pos,
+ &free_data_pos,
+ NULL,
+ &evaluate_pos,
+ NULL,
+ {"com of ATOM_EXPR [pbc]", 0, NULL},
+};
+
+/*!
+ * \param[in] npar Should be 1 or 2.
+ * \param[in,out] param Method parameters (should point to
+ * \ref smparams_keyword_pos or \ref smparams_com).
+ * \returns Pointer to the allocated data (\c t_methoddata_pos).
+ *
+ * Allocates memory for a \c t_methoddata_pos structure and initializes
+ * the first parameter to define the value for \c t_methoddata_pos::g.
+ * If a second parameter is present, it is used for setting the
+ * \c t_methoddata_pos::bPBC flag.
+ */
+static void *
+init_data_pos(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_pos *data;
+
+ snew(data, 1);
+ param[0].val.u.g = &data->g;
+ if (npar > 1)
+ {
+ param[1].val.u.b = &data->bPBC;
+ }
+ data->pc = NULL;
+ data->bPBC = FALSE;
+ data->type = NULL;
+ data->flags = -1;
+ return data;
+}
+
+/*!
+ * \param[in] pcc Position calculation collection to use.
+ * \param[in,out] data Should point to \c t_methoddata_pos.
+ */
+static void
+set_poscoll_pos(gmx_ana_poscalc_coll_t *pcc, void *data)
+{
+ ((t_methoddata_pos *)data)->pcc = pcc;
+}
+
+/*!
+ * \param[in,out] sel Selection element to initialize.
+ * \param[in] type One of the enum values acceptable for
+ * gmx_ana_poscalc_type_from_enum().
+ *
+ * Initializes the reference position type for position evaluation.
+ * If called multiple times, the first setting takes effect, and later calls
+ * are neglected.
+ */
+void
+_gmx_selelem_set_kwpos_type(t_selelem *sel, const char *type)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)sel->u.expr.mdata;
+
+ if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
+ || sel->u.expr.method->name != sm_keyword_pos.name)
+ {
+ return;
+ }
+ if (!d->type && type)
+ {
+ d->type = strdup(type);
+ /* FIXME: It would be better not to have the string here hardcoded. */
+ if (type[0] != 'a')
+ {
+ sel->u.expr.method->flags |= SMETH_REQTOP;
+ }
+ }
+}
+
+/*!
+ * \param[in,out] sel Selection element to initialize.
+ * \param[in] flags Default completion flags
+ * (see gmx_ana_poscalc_type_from_enum()).
+ *
+ * Initializes the flags for position evaluation.
+ * If called multiple times, the first setting takes effect, and later calls
+ * are neglected.
+ */
+void
+_gmx_selelem_set_kwpos_flags(t_selelem *sel, int flags)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)sel->u.expr.mdata;
+
+ if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
+ || sel->u.expr.method->name != sm_keyword_pos.name)
+ {
+ return;
+ }
+ if (d->flags == -1)
+ {
+ d->flags = flags;
+ }
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] npar Not used.
+ * \param[in] param Not used.
+ * \param[in,out] data Should point to \c t_methoddata_pos.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * The \c t_methoddata_pos::type field should have been initialized
+ * externally using _gmx_selelem_set_kwpos_type().
+ */
+static int
+init_kwpos(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)data;
+ int rc;
+
+ if (!(param[0].flags & SPAR_DYNAMIC))
+ {
+ d->flags &= ~(POS_DYNAMIC | POS_MASKONLY);
+ }
+ else if (!(d->flags & POS_MASKONLY))
+ {
+ d->flags |= POS_DYNAMIC;
+ }
+ rc = gmx_ana_poscalc_create_enum(&d->pc, d->pcc, d->type, d->flags);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_poscalc_set_maxindex(d->pc, &d->g);
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology data structure.
+ * \param[in] npar Not used.
+ * \param[in] param Not used.
+ * \param[in,out] data Should point to \c t_methoddata_pos.
+ * \returns 0 on success, a non-zero error code on error.
+ */
+static int
+init_cog(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)data;
+ int rc;
+
+ d->flags = (param[0].flags & SPAR_DYNAMIC) ? POS_DYNAMIC : 0;
+ rc = gmx_ana_poscalc_create(&d->pc, d->pcc, d->bPBC ? POS_ALL_PBC : POS_ALL,
+ d->flags);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_poscalc_set_maxindex(d->pc, &d->g);
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology data structure.
+ * \param[in] npar Not used.
+ * \param[in] param Not used.
+ * \param[in,out] data Should point to \c t_methoddata_pos.
+ * \returns 0 on success, a non-zero error code on error.
+ */
+static int
+init_com(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)data;
+ int rc;
+
+ d->flags = (param[0].flags & SPAR_DYNAMIC) ? POS_DYNAMIC : 0;
+ d->flags |= POS_MASS;
+ rc = gmx_ana_poscalc_create(&d->pc, d->pcc, d->bPBC ? POS_ALL_PBC : POS_ALL,
+ d->flags);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ gmx_ana_poscalc_set_maxindex(d->pc, &d->g);
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology data structure.
+ * \param[in,out] out Pointer to output data structure.
+ * \param[in,out] data Should point to \c t_methoddata_pos.
+ * \returns 0 for success.
+ */
+static int
+init_output_pos(t_topology *top, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)data;
+
+ gmx_ana_poscalc_init_pos(d->pc, out->u.p);
+ gmx_ana_pos_set_evalgrp(out->u.p, &d->g);
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \c t_methoddata_pos).
+ *
+ * Frees the memory allocated for \c t_methoddata_pos::g and
+ * \c t_methoddata_pos::pc.
+ */
+static void
+free_data_pos(void *data)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)data;
+
+ sfree(d->type);
+ gmx_ana_poscalc_free(d->pc);
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_pos.
+ *
+ * Calculates the positions using \c t_methoddata_pos::pc for the index group
+ * in \c t_methoddata_pos::g and stores the results in \p out->u.p.
+ */
+static int
+evaluate_pos(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_pos *d = (t_methoddata_pos *)data;
+
+ gmx_ana_poscalc_update(d->pc, out->u.p, &d->g, fr, pbc);
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements the \p same selection method.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <macros.h>
+#include <smalloc.h>
+#include <string2.h>
+
+#include "gromacs/selection/selmethod.h"
+
+#include "keywords.h"
+#include "parsetree.h"
+#include "selelem.h"
+
+/*! \internal \brief
+ * Data structure for the \p same selection method.
+ *
+ * To avoid duplicate initialization code, the same data structure is used
+ * for matching both integer and string keywords; hence the unions.
+ */
+typedef struct
+{
+ /** Value for each atom to match. */
+ union
+ {
+ int *i;
+ char **s;
+ void *ptr;
+ } val;
+ /*! \brief
+ * Number of values in the \p as array.
+ *
+ * For string values, this is actually the number of values in the
+ * \p as_s_sorted array.
+ */
+ int nas;
+ /** Values to match against. */
+ union
+ {
+ int *i;
+ char **s;
+ void *ptr;
+ } as;
+ /*! \brief
+ * Separate array for sorted \p as.s array.
+ *
+ * The array of strings returned as the output value of a parameter should
+ * not be messed with to avoid memory corruption (the pointers in the array
+ * may be reused for several evaluations), so we keep our own copy for
+ * modifications.
+ */
+ char **as_s_sorted;
+ /** Whether simple matching can be used. */
+ gmx_bool bSorted;
+} t_methoddata_same;
+
+/** Allocates data for the \p same selection method. */
+static void *
+init_data_same(int npar, gmx_ana_selparam_t *param);
+/** Initializes the \p same selection method. */
+static int
+init_same(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Frees the data allocated for the \p same selection method. */
+static void
+free_data_same(void *data);
+/** Initializes the evaluation of the \p same selection method for a frame. */
+static int
+init_frame_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
+/** Evaluates the \p same selection method. */
+static int
+evaluate_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Initializes the evaluation of the \p same selection method for a frame. */
+static int
+init_frame_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
+/** Evaluates the \p same selection method. */
+static int
+evaluate_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+
+/** Parameters for the \p same selection method. */
+static gmx_ana_selparam_t smparams_same_int[] = {
+ {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_ATOMVAL},
+ {"as", {INT_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+};
+
+/** Parameters for the \p same selection method. */
+static gmx_ana_selparam_t smparams_same_str[] = {
+ {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_ATOMVAL},
+ {"as", {STR_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+};
+
+/** Help text for the \p same selection method. */
+static const char *help_same[] = {
+ "EXTENDING SELECTIONS[PAR]",
+
+ "[TT]same KEYWORD as ATOM_EXPR[tt][PAR]",
+
+ "The keyword [TT]same[tt] can be used to select all atoms for which",
+ "the given [TT]KEYWORD[tt] matches any of the atoms in [TT]ATOM_EXPR[tt].",
+ "Keywords that evaluate to integer or string values are supported.",
+};
+
+/*! \internal \brief Selection method data for the \p same method. */
+gmx_ana_selmethod_t sm_same = {
+ "same", GROUP_VALUE, 0,
+ asize(smparams_same_int), smparams_same_int,
+ &init_data_same,
+ NULL,
+ &init_same,
+ NULL,
+ &free_data_same,
+ &init_frame_same_int,
+ &evaluate_same_int,
+ NULL,
+ {"same KEYWORD as ATOM_EXPR", asize(help_same), help_same},
+};
+
+/*! \brief
+ * Selection method data for the \p same method.
+ *
+ * This selection method is used for matching string keywords. The parser
+ * never sees this method; _gmx_selelem_custom_init_same() replaces sm_same
+ * with this method in cases where it is required.
+ */
+static gmx_ana_selmethod_t sm_same_str = {
+ "same", GROUP_VALUE, SMETH_SINGLEVAL,
+ asize(smparams_same_str), smparams_same_str,
+ &init_data_same,
+ NULL,
+ &init_same,
+ NULL,
+ &free_data_same,
+ &init_frame_same_str,
+ &evaluate_same_str,
+ NULL,
+ {"same KEYWORD as ATOM_EXPR", asize(help_same), help_same},
+};
+
+/*!
+ * \param[in] npar Not used (should be 2).
+ * \param[in,out] param Method parameters (should point to
+ * \ref smparams_same).
+ * \returns Pointer to the allocated data (\ref t_methoddata_same).
+ */
+static void *
+init_data_same(int npar, gmx_ana_selparam_t *param)
+{
+ t_methoddata_same *data;
+
+ snew(data, 1);
+ data->as_s_sorted = NULL;
+ param[1].nvalptr = &data->nas;
+ return data;
+}
+
+/*!
+ * \param[in,out] method The method to initialize.
+ * \param[in,out] params Pointer to the first parameter.
+ * \param[in] scanner Scanner data structure.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * If \p *method is not a \c same method, this function returns zero
+ * immediately.
+ */
+int
+_gmx_selelem_custom_init_same(gmx_ana_selmethod_t **method,
+ t_selexpr_param *params,
+ void *scanner)
+{
+ gmx_ana_selmethod_t *kwmethod;
+ t_selelem *kwelem;
+ t_selexpr_param *param;
+ char *pname;
+ int rc;
+
+ /* Do nothing if this is not a same method. */
+ if (!*method || (*method)->name != sm_same.name)
+ {
+ return 0;
+ }
+
+ if (params->nval != 1 || !params->value->bExpr
+ || params->value->u.expr->type != SEL_EXPRESSION)
+ {
+ _gmx_selparser_error(scanner, "'same' should be followed by a single keyword");
+ return -1;
+ }
+ kwmethod = params->value->u.expr->u.expr.method;
+
+ if (kwmethod->type == STR_VALUE)
+ {
+ *method = &sm_same_str;
+ }
+
+ /* We do custom processing with the second parameter, so remove it from
+ * the params list, but save the name for later. */
+ param = params->next;
+ params->next = NULL;
+ pname = param->name;
+ param->name = NULL;
+ /* Create a second keyword evaluation element for the keyword given as
+ * the first parameter, evaluating the keyword in the group given by the
+ * second parameter. */
+ rc = _gmx_sel_init_keyword_evaluator(&kwelem, kwmethod, param, scanner);
+ if (rc != 0)
+ {
+ sfree(pname);
+ return rc;
+ }
+ /* Replace the second parameter with one with a value from \p kwelem. */
+ param = _gmx_selexpr_create_param(pname);
+ param->nval = 1;
+ param->value = _gmx_selexpr_create_value_expr(kwelem);
+ params->next = param;
+ return 0;
+}
+
+/*!
+ * \param top Not used.
+ * \param npar Not used (should be 2).
+ * \param param Initialized method parameters (should point to a copy of
+ * \ref smparams_same).
+ * \param data Pointer to \ref t_methoddata_same to initialize.
+ * \returns 0 on success, -1 on failure.
+ */
+static int
+init_same(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ t_methoddata_same *d = (t_methoddata_same *)data;
+
+ d->val.ptr = param[0].val.u.ptr;
+ d->as.ptr = param[1].val.u.ptr;
+ if (param[1].val.type == STR_VALUE)
+ {
+ snew(d->as_s_sorted, d->nas);
+ }
+ if (!(param[0].flags & SPAR_ATOMVAL))
+ {
+ fprintf(stderr, "ERROR: the same selection keyword combined with a "
+ "non-keyword does not make sense\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * \param data Data to free (should point to a \ref t_methoddata_same).
+ */
+static void
+free_data_same(void *data)
+{
+ t_methoddata_same *d = (t_methoddata_same *)data;
+
+ sfree(d->as_s_sorted);
+}
+
+/*! \brief
+ * Helper function for comparison of two integers.
+ */
+static int
+cmp_int(const void *a, const void *b)
+{
+ if (*(int *)a < *(int *)b)
+ {
+ return -1;
+ }
+ if (*(int *)a > *(int *)b)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Current frame.
+ * \param[in] pbc PBC structure.
+ * \param data Should point to a \ref t_methoddata_same.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Sorts the \c data->as.i array and removes identical values for faster and
+ * simpler lookup.
+ */
+static int
+init_frame_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
+{
+ t_methoddata_same *d = (t_methoddata_same *)data;
+ int i, j;
+
+ /* Collapse adjacent values, and check whether the array is sorted. */
+ d->bSorted = TRUE;
+ for (i = 1, j = 0; i < d->nas; ++i)
+ {
+ if (d->as.i[i] != d->as.i[j])
+ {
+ if (d->as.i[i] < d->as.i[j])
+ {
+ d->bSorted = FALSE;
+ }
+ ++j;
+ d->as.i[j] = d->as.i[i];
+ }
+ }
+ d->nas = j + 1;
+
+ if (!d->bSorted)
+ {
+ qsort(d->as.i, d->nas, sizeof(d->as.i[0]), &cmp_int);
+ /* More identical values may become adjacent after sorting. */
+ for (i = 1, j = 0; i < d->nas; ++i)
+ {
+ if (d->as.i[i] != d->as.i[j])
+ {
+ ++j;
+ d->as.i[j] = d->as.i[i];
+ }
+ }
+ d->nas = j + 1;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_same.
+ *
+ * Calculates which values in \c data->val.i can be found in \c data->as.i
+ * (assumed sorted), and writes the corresponding atoms to output.
+ * If \c data->val is sorted, uses a linear scan of both arrays, otherwise a
+ * binary search of \c data->as is performed for each block of values in
+ * \c data->val.
+ */
+static int
+evaluate_same_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_same *d = (t_methoddata_same *)data;
+ int i, j;
+
+ out->u.g->isize = 0;
+ i = j = 0;
+ while (j < g->isize)
+ {
+ if (d->bSorted)
+ {
+ /* If we are sorted, we can do a simple linear scan. */
+ while (i < d->nas && d->as.i[i] < d->val.i[j]) ++i;
+ }
+ else
+ {
+ /* If not, we must do a binary search of all the values. */
+ int i1, i2;
+
+ i1 = 0;
+ i2 = d->nas;
+ while (i2 - i1 > 1)
+ {
+ int itry = (i1 + i2) / 2;
+ if (d->as.i[itry] <= d->val.i[j])
+ {
+ i1 = itry;
+ }
+ else
+ {
+ i2 = itry;
+ }
+ }
+ i = (d->as.i[i1] == d->val.i[j] ? i1 : d->nas);
+ }
+ /* Check whether the value was found in the as list. */
+ if (i == d->nas || d->as.i[i] != d->val.i[j])
+ {
+ /* If not, skip all atoms with the same value. */
+ int tmpval = d->val.i[j];
+ ++j;
+ while (j < g->isize && d->val.i[j] == tmpval) ++j;
+ }
+ else
+ {
+ /* Copy all the atoms with this value to the output. */
+ while (j < g->isize && d->val.i[j] == d->as.i[i])
+ {
+ out->u.g->index[out->u.g->isize++] = g->index[j];
+ ++j;
+ }
+ }
+ if (j < g->isize && d->val.i[j] < d->val.i[j - 1])
+ {
+ d->bSorted = FALSE;
+ }
+ }
+ return 0;
+}
+
+/*! \brief
+ * Helper function for comparison of two strings.
+ */
+static int
+cmp_str(const void *a, const void *b)
+{
+ return strcmp(*(char **)a, *(char **)b);
+}
+
+/*!
+ * \param[in] top Not used.
+ * \param[in] fr Current frame.
+ * \param[in] pbc PBC structure.
+ * \param data Should point to a \ref t_methoddata_same.
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * Sorts the \c data->as.s array and removes identical values for faster and
+ * simpler lookup.
+ */
+static int
+init_frame_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
+{
+ t_methoddata_same *d = (t_methoddata_same *)data;
+ int i, j;
+
+ /* Collapse adjacent values.
+ * For strings, it's unlikely that the values would be sorted originally,
+ * so set bSorted always to FALSE. */
+ d->bSorted = FALSE;
+ d->as_s_sorted[0] = d->as.s[0];
+ for (i = 1, j = 0; i < d->nas; ++i)
+ {
+ if (strcmp(d->as.s[i], d->as_s_sorted[j]) != 0)
+ {
+ ++j;
+ d->as_s_sorted[j] = d->as.s[i];
+ }
+ }
+ d->nas = j + 1;
+
+ qsort(d->as_s_sorted, d->nas, sizeof(d->as_s_sorted[0]), &cmp_str);
+ /* More identical values may become adjacent after sorting. */
+ for (i = 1, j = 0; i < d->nas; ++i)
+ {
+ if (strcmp(d->as_s_sorted[i], d->as_s_sorted[j]) != 0)
+ {
+ ++j;
+ d->as_s_sorted[j] = d->as_s_sorted[i];
+ }
+ }
+ d->nas = j + 1;
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data should point to a \c t_methoddata_same.
+ *
+ * Calculates which strings in \c data->val.s can be found in \c data->as.s
+ * (assumed sorted), and writes the corresponding atoms to output.
+ * A binary search of \c data->as is performed for each block of values in
+ * \c data->val.
+ */
+static int
+evaluate_same_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ t_methoddata_same *d = (t_methoddata_same *)data;
+ int i, j;
+
+ out->u.g->isize = 0;
+ j = 0;
+ while (j < g->isize)
+ {
+ /* Do a binary search of the strings. */
+ void *ptr;
+ ptr = bsearch(&d->val.s[j], d->as_s_sorted, d->nas,
+ sizeof(d->as_s_sorted[0]), &cmp_str);
+ /* Check whether the value was found in the as list. */
+ if (ptr == NULL)
+ {
+ /* If not, skip all atoms with the same value. */
+ const char *tmpval = d->val.s[j];
+ ++j;
+ while (j < g->isize && strcmp(d->val.s[j], tmpval) == 0) ++j;
+ }
+ else
+ {
+ const char *tmpval = d->val.s[j];
+ /* Copy all the atoms with this value to the output. */
+ while (j < g->isize && strcmp(d->val.s[j], tmpval) == 0)
+ {
+ out->u.g->index[out->u.g->isize++] = g->index[j];
+ ++j;
+ }
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements simple keyword selection methods.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gromacs/selection/position.h"
+#include "gromacs/selection/selmethod.h"
+
+/** Evaluates the \p all selection keyword. */
+static int
+evaluate_all(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p none selection keyword. */
+static int
+evaluate_none(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p atomnr selection keyword. */
+static int
+evaluate_atomnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p resnr selection keyword. */
+static int
+evaluate_resnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p resindex selection keyword. */
+static int
+evaluate_resindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Checks whether molecule information is present in the topology. */
+static int
+check_molecules(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Evaluates the \p molindex selection keyword. */
+static int
+evaluate_molindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p name selection keyword. */
+static int
+evaluate_atomname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Checks whether atom types are present in the topology. */
+static int
+check_atomtype(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Evaluates the \p type selection keyword. */
+static int
+evaluate_atomtype(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p insertcode selection keyword. */
+static int
+evaluate_insertcode(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p chain selection keyword. */
+static int
+evaluate_chain(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p mass selection keyword. */
+static int
+evaluate_mass(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p charge selection keyword. */
+static int
+evaluate_charge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Checks whether PDB info is present in the topology. */
+static int
+check_pdbinfo(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
+/** Evaluates the \p altloc selection keyword. */
+static int
+evaluate_altloc(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p occupancy selection keyword. */
+static int
+evaluate_occupancy(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p betafactor selection keyword. */
+static int
+evaluate_betafactor(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p resname selection keyword. */
+static int
+evaluate_resname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
+
+/** Evaluates the \p x selection keyword. */
+static int
+evaluate_x(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p y selection keyword. */
+static int
+evaluate_y(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
+/** Evaluates the \p z selection keyword. */
+static int
+evaluate_z(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data);
+
+/** \internal Selection method data for \p all selection keyword. */
+gmx_ana_selmethod_t sm_all = {
+ "all", GROUP_VALUE, 0,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_all,
+ NULL,
+};
+
+/** \internal Selection method data for \p none selection keyword. */
+gmx_ana_selmethod_t sm_none = {
+ "none", GROUP_VALUE, 0,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_none,
+ NULL,
+};
+
+/** \internal Selection method data for \p atomnr selection keyword. */
+gmx_ana_selmethod_t sm_atomnr = {
+ "atomnr", INT_VALUE, 0,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_atomnr,
+ NULL,
+};
+
+/** \internal Selection method data for \p resnr selection keyword. */
+gmx_ana_selmethod_t sm_resnr = {
+ "resnr", INT_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_resnr,
+ NULL,
+};
+
+/** \internal Selection method data for \p resindex selection keyword. */
+gmx_ana_selmethod_t sm_resindex = {
+ "resindex", INT_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_resindex,
+ NULL,
+};
+
+/** \internal Selection method data for \p molindex selection keyword. */
+gmx_ana_selmethod_t sm_molindex = {
+ "molindex", INT_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ &check_molecules,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_molindex,
+ NULL,
+};
+
+/** \internal Selection method data for \p name selection keyword. */
+gmx_ana_selmethod_t sm_atomname = {
+ "name", STR_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_atomname,
+ NULL,
+};
+
+/** \internal Selection method data for \p type selection keyword. */
+gmx_ana_selmethod_t sm_atomtype = {
+ "type", STR_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ &check_atomtype,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_atomtype,
+ NULL,
+};
+
+/** \internal Selection method data for \p resname selection keyword. */
+gmx_ana_selmethod_t sm_resname = {
+ "resname", STR_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_resname,
+ NULL,
+};
+
+/** \internal Selection method data for \p chain selection keyword. */
+gmx_ana_selmethod_t sm_insertcode = {
+ "insertcode", STR_VALUE, SMETH_REQTOP | SMETH_CHARVAL,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_insertcode,
+ NULL,
+};
+
+/** \internal Selection method data for \p chain selection keyword. */
+gmx_ana_selmethod_t sm_chain = {
+ "chain", STR_VALUE, SMETH_REQTOP | SMETH_CHARVAL,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_chain,
+ NULL,
+};
+
+/** \internal Selection method data for \p mass selection keyword. */
+gmx_ana_selmethod_t sm_mass = {
+ "mass", REAL_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_mass,
+ NULL,
+};
+
+/** \internal Selection method data for \p charge selection keyword. */
+gmx_ana_selmethod_t sm_charge = {
+ "charge", REAL_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_charge,
+ NULL,
+};
+
+/** \internal Selection method data for \p chain selection keyword. */
+gmx_ana_selmethod_t sm_altloc = {
+ "altloc", STR_VALUE, SMETH_REQTOP | SMETH_CHARVAL,
+ 0, NULL,
+ NULL,
+ NULL,
+ &check_pdbinfo,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_altloc,
+ NULL,
+};
+
+/** \internal Selection method data for \p occupancy selection keyword. */
+gmx_ana_selmethod_t sm_occupancy = {
+ "occupancy", REAL_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ &check_pdbinfo,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_occupancy,
+ NULL,
+};
+
+/** \internal Selection method data for \p betafactor selection keyword. */
+gmx_ana_selmethod_t sm_betafactor = {
+ "betafactor", REAL_VALUE, SMETH_REQTOP,
+ 0, NULL,
+ NULL,
+ NULL,
+ &check_pdbinfo,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_betafactor,
+ NULL,
+};
+
+/** \internal Selection method data for \p x selection keyword. */
+gmx_ana_selmethod_t sm_x = {
+ "x", REAL_VALUE, SMETH_DYNAMIC,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_x,
+};
+
+/** \internal Selection method data for \p y selection keyword. */
+gmx_ana_selmethod_t sm_y = {
+ "y", REAL_VALUE, SMETH_DYNAMIC,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_y,
+};
+
+/** \internal Selection method data for \p z selection keyword. */
+gmx_ana_selmethod_t sm_z = {
+ "z", REAL_VALUE, SMETH_DYNAMIC,
+ 0, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &evaluate_z,
+};
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Copies \p g to \p out->u.g.
+ */
+static int
+evaluate_all(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ gmx_ana_index_copy(out->u.g, g, FALSE);
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns an empty \p out->u.g.
+ */
+static int
+evaluate_none(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ out->u.g->isize = 0;
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the indices for each atom in \p out->u.i.
+ */
+static int
+evaluate_atomnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.i[i] = g->index[i] + 1;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the residue numbers for each atom in \p out->u.i.
+ */
+static int
+evaluate_resnr(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+ int resind;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ resind = top->atoms.atom[g->index[i]].resind;
+ out->u.i[i] = top->atoms.resinfo[resind].nr;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the residue indices for each atom in \p out->u.i.
+ */
+static int
+evaluate_resindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.i[i] = top->atoms.atom[g->index[i]].resind + 1;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure.
+ * \param npar Not used.
+ * \param param Not used.
+ * \param data Not used.
+ * \returns 0 if molecule info is present in the topology, -1 otherwise.
+ *
+ * If molecule information is not found, also prints an error message.
+ */
+static int
+check_molecules(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ gmx_bool bOk;
+
+ bOk = (top != NULL && top->mols.nr > 0);
+ if (!bOk)
+ {
+ fprintf(stderr, "Molecule information not available in topology!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the molecule indices for each atom in \p out->u.i.
+ */
+static int
+evaluate_molindex(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i, j;
+
+ out->nr = g->isize;
+ for (i = j = 0; i < g->isize; ++i)
+ {
+ while (top->mols.index[j + 1] <= g->index[i]) ++j;
+ out->u.i[i] = j + 1;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the atom name for each atom in \p out->u.s.
+ */
+static int
+evaluate_atomname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.s[i] = *top->atoms.atomname[g->index[i]];
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure.
+ * \param npar Not used.
+ * \param param Not used.
+ * \param data Not used.
+ * \returns 0 if atom types are present in the topology, -1 otherwise.
+ *
+ * If the atom types are not found, also prints an error message.
+ */
+static int
+check_atomtype(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ gmx_bool bOk;
+
+ bOk = (top != NULL && top->atoms.atomtype != NULL);
+ if (!bOk)
+ {
+ fprintf(stderr, "Atom types not available in topology!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the atom type for each atom in \p out->u.s.
+ * Segfaults if atom types are not found in the topology.
+ */
+static int
+evaluate_atomtype(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.s[i] = *top->atoms.atomtype[g->index[i]];
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the residue name for each atom in \p out->u.s.
+ */
+static int
+evaluate_resname(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+ int resind;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ resind = top->atoms.atom[g->index[i]].resind;
+ out->u.s[i] = *top->atoms.resinfo[resind].name;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the insertion code for each atom in \p out->u.s.
+ */
+static int
+evaluate_insertcode(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+ int resind;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ resind = top->atoms.atom[g->index[i]].resind;
+ out->u.s[i][0] = top->atoms.resinfo[resind].ic;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the chain for each atom in \p out->u.s.
+ */
+static int
+evaluate_chain(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+ int resind;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ resind = top->atoms.atom[g->index[i]].resind;
+ out->u.s[i][0] = top->atoms.resinfo[resind].chainid;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the mass for each atom in \p out->u.r.
+ */
+static int
+evaluate_mass(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.r[i] = top->atoms.atom[g->index[i]].m;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the charge for each atom in \p out->u.r.
+ */
+static int
+evaluate_charge(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.r[i] = top->atoms.atom[g->index[i]].q;
+ }
+ return 0;
+}
+
+/*!
+ * \param[in] top Topology structure.
+ * \param npar Not used.
+ * \param param Not used.
+ * \param data Not used.
+ * \returns 0 if PDB info is present in the topology, -1 otherwise.
+ *
+ * If PDB info is not found, also prints an error message.
+ */
+static int
+check_pdbinfo(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
+{
+ gmx_bool bOk;
+
+ bOk = (top != NULL && top->atoms.pdbinfo != NULL);
+ if (!bOk)
+ {
+ fprintf(stderr, "PDB info not available in topology!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the alternate location identifier for each atom in \p out->u.s.
+ */
+static int
+evaluate_altloc(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.s[i][0] = top->atoms.pdbinfo[g->index[i]].altloc;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the occupancy numbers for each atom in \p out->u.r.
+ * Segfaults if PDB info is not found in the topology.
+ */
+static int
+evaluate_occupancy(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.r[i] = top->atoms.pdbinfo[g->index[i]].occup;
+ }
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the B-factors for each atom in \p out->u.r.
+ * Segfaults if PDB info is not found in the topology.
+ */
+static int
+evaluate_betafactor(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
+{
+ int i;
+
+ out->nr = g->isize;
+ for (i = 0; i < g->isize; ++i)
+ {
+ out->u.r[i] = top->atoms.pdbinfo[g->index[i]].bfac;
+ }
+ return 0;
+}
+
+/*! \brief
+ * Internal utility function for position keyword evaluation.
+ *
+ * \param[in] fr Current frame.
+ * \param[in] g Index group for which the coordinates should be evaluated.
+ * \param[out] out Output array.
+ * \param[in] pos Position data to use instead of atomic coordinates
+ * (can be NULL).
+ * \param[in] d Coordinate index to evaluate (\p XX, \p YY or \p ZZ).
+ *
+ * This function is used internally by evaluate_x(), evaluate_y() and
+ * evaluate_z() to do the actual evaluation.
+ */
+static void
+evaluate_coord(t_trxframe *fr, gmx_ana_index_t *g, real out[],
+ gmx_ana_pos_t *pos, int d)
+{
+ int b, i;
+ real v;
+
+ if (pos)
+ {
+ for (b = 0; b < pos->nr; ++b)
+ {
+ v = pos->x[b][d];
+ for (i = pos->m.mapb.index[b]; i < pos->m.mapb.index[b+1]; ++i)
+ {
+ out[i] = v;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < g->isize; ++i)
+ {
+ out[i] = fr->x[g->index[i]][d];
+ }
+ }
+}
+
+/*!
+ * See sel_updatefunc_pos() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the \p x coordinate for each atom in \p out->u.r.
+ */
+static int
+evaluate_x(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
+{
+ out->nr = pos->g->isize;
+ evaluate_coord(fr, pos->g, out->u.r, pos, XX);
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the \p y coordinate for each atom in \p out->u.r.
+ */
+static int
+evaluate_y(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
+{
+ out->nr = pos->g->isize;
+ evaluate_coord(fr, pos->g, out->u.r, pos, YY);
+ return 0;
+}
+
+/*!
+ * See sel_updatefunc() for description of the parameters.
+ * \p data is not used.
+ *
+ * Returns the \p z coordinate for each atom in \p out->u.r.
+ */
+static int
+evaluate_z(t_topology *top, t_trxframe *fr, t_pbc *pbc,
+ gmx_ana_pos_t *pos, gmx_ana_selvalue_t *out, void *data)
+{
+ out->nr = pos->g->isize;
+ evaluate_coord(fr, pos->g, out->u.r, pos, ZZ);
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in symrec.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <macros.h>
+#include <smalloc.h>
+#include <string2.h>
+#include <typedefs.h>
+#include <gmx_fatal.h>
+
+#include "gromacs/selection/poscalc.h"
+
+#include "selelem.h"
+#include "symrec.h"
+
+/*! \internal \brief
+ * Symbol table for the selection parser.
+ */
+struct gmx_sel_symtab_t
+{
+ /** Pointer to the first symbol in the linked list of symbols. */
+ gmx_sel_symrec_t *first;
+};
+
+/*! \internal \brief
+ * Single symbol for the selection parser.
+ */
+struct gmx_sel_symrec_t
+{
+ /** Name of the symbol. */
+ char *name;
+ /** Type of the symbol. */
+ e_symbol_t type;
+ /** Value of the symbol. */
+ union {
+ /** Pointer to the method structure (\ref SYMBOL_METHOD). */
+ struct gmx_ana_selmethod_t *meth;
+ /** Pointer to the variable value (\ref SYMBOL_VARIABLE). */
+ struct t_selelem *var;
+ } u;
+ /** Pointer to the next symbol. */
+ struct gmx_sel_symrec_t *next;
+};
+
+/** List of reserved symbols to register in add_reserved_symbols(). */
+static const char *const sym_reserved[] = {
+ "group",
+ "to",
+ "not",
+ "and",
+ "or",
+ "xor",
+ "yes",
+ "no",
+ "on",
+ "off",
+ "help",
+};
+
+/*!
+ * \param[in] sym Symbol to query.
+ * \returns The name of \p sym.
+ *
+ * The returned pointer should not be free'd.
+ */
+char *
+_gmx_sel_sym_name(gmx_sel_symrec_t *sym)
+{
+ return sym->name;
+}
+
+/*!
+ * \param[in] sym Symbol to query.
+ * \returns The type of \p sym.
+ */
+e_symbol_t
+_gmx_sel_sym_type(gmx_sel_symrec_t *sym)
+{
+ return sym->type;
+}
+
+/*!
+ * \param[in] sym Symbol to query.
+ * \returns The method associated with \p sym, or NULL if \p sym is not a
+ * \ref SYMBOL_METHOD symbol.
+ */
+struct gmx_ana_selmethod_t *
+_gmx_sel_sym_value_method(gmx_sel_symrec_t *sym)
+{
+ if (sym->type != SYMBOL_METHOD)
+ {
+ gmx_call("symbol is not a method symbol");
+ return NULL;
+ }
+ return sym->u.meth;
+}
+
+/*!
+ * \param[in] sym Symbol to query.
+ * \returns The variable expression associated with \p sym, or NULL if
+ * \p sym is not a \ref SYMBOL_VARIABLE symbol.
+ */
+struct t_selelem *
+_gmx_sel_sym_value_var(gmx_sel_symrec_t *sym)
+{
+ if (sym->type != SYMBOL_VARIABLE)
+ {
+ gmx_call("symbol is not a variable symbol");
+ return NULL;
+ }
+ return sym->u.var;
+}
+
+/*! \brief
+ * Adds the reserved symbols to a symbol table.
+ *
+ * \param[in,out] tab Symbol table to which the symbols are added.
+ *
+ * Assumes that the symbol table is empty.
+ */
+static void
+add_reserved_symbols(gmx_sel_symtab_t *tab)
+{
+ gmx_sel_symrec_t *sym;
+ gmx_sel_symrec_t *last;
+ size_t i;
+
+ last = NULL;
+ for (i = 0; i < asize(sym_reserved); ++i)
+ {
+ snew(sym, 1);
+ sym->name = strdup(sym_reserved[i]);
+ sym->type = SYMBOL_RESERVED;
+ sym->next = NULL;
+ if (last)
+ {
+ last->next = sym;
+ }
+ else
+ {
+ tab->first = sym;
+ }
+ last = sym;
+ }
+}
+
+/*! \brief
+ * Adds the position symbols to the symbol list.
+ *
+ * \param[in,out] tab Symbol table to which the symbols are added.
+ */
+static void
+add_position_symbols(gmx_sel_symtab_t *tab)
+{
+ const char **postypes;
+ gmx_sel_symrec_t *sym;
+ gmx_sel_symrec_t *last;
+ int i;
+
+ postypes = gmx_ana_poscalc_create_type_enum(TRUE);
+ last = tab->first;
+ while (last && last->next)
+ {
+ last = last->next;
+ }
+ for (i = 1; postypes[i] != NULL; ++i)
+ {
+ snew(sym, 1);
+ sym->name = strdup(postypes[i]);
+ sym->type = SYMBOL_POS;
+ sym->next = NULL;
+ if (last)
+ {
+ last->next = sym;
+ }
+ else
+ {
+ tab->first = sym;
+ }
+ last = sym;
+ }
+ sfree(postypes);
+}
+
+/*!
+ * \param[out] tabp Symbol table pointer to initialize.
+ *
+ * Reserved and position symbols are added to the created table.
+ */
+int
+_gmx_sel_symtab_create(gmx_sel_symtab_t **tabp)
+{
+ gmx_sel_symtab_t *tab;
+
+ snew(tab, 1);
+ add_reserved_symbols(tab);
+ add_position_symbols(tab);
+ *tabp = tab;
+ return 0;
+}
+
+/*!
+ * \param[in] tab Symbol table to free.
+ *
+ * The pointer \p tab is invalid after the call.
+ */
+void
+_gmx_sel_symtab_free(gmx_sel_symtab_t *tab)
+{
+ gmx_sel_symrec_t *sym;
+
+ while (tab->first)
+ {
+ sym = tab->first;
+ tab->first = sym->next;
+ if (sym->type == SYMBOL_VARIABLE)
+ {
+ _gmx_selelem_free(sym->u.var);
+ }
+ sfree(sym->name);
+ sfree(sym);
+ }
+ sfree(tab);
+}
+
+/*!
+ * \param[in] tab Symbol table to search.
+ * \param[in] name Symbol name to find.
+ * \param[in] bExact If FALSE, symbols that begin with \p name are also
+ * considered.
+ * \returns Pointer to the symbol with name \p name, or NULL if not found.
+ *
+ * If no exact match is found and \p bExact is FALSE, returns a symbol that
+ * begins with \p name if a unique matching symbol is found.
+ */
+gmx_sel_symrec_t *
+_gmx_sel_find_symbol(gmx_sel_symtab_t *tab, const char *name, gmx_bool bExact)
+{
+ return _gmx_sel_find_symbol_len(tab, name, strlen(name), bExact);
+}
+
+/*!
+ * \param[in] tab Symbol table to search.
+ * \param[in] name Symbol name to find.
+ * \param[in] len Only consider the first \p len characters of \p name.
+ * \param[in] bExact If FALSE, symbols that begin with \p name are also
+ * considered.
+ * \returns Pointer to the symbol with name \p name, or NULL if not found.
+ *
+ * If no exact match is found and \p bExact is FALSE, returns a symbol that
+ * begins with \p name if a unique matching symbol is found.
+ *
+ * The parameter \p len is there to allow using this function from scanner.l
+ * without modifying the text to be scanned or copying it.
+ */
+gmx_sel_symrec_t *
+_gmx_sel_find_symbol_len(gmx_sel_symtab_t *tab, const char *name, size_t len,
+ gmx_bool bExact)
+{
+ gmx_sel_symrec_t *sym;
+ gmx_sel_symrec_t *match;
+ gmx_bool bUnique;
+ gmx_bool bMatch;
+
+ match = NULL;
+ bUnique = TRUE;
+ bMatch = FALSE;
+ sym = tab->first;
+ while (sym)
+ {
+ if (!strncmp(sym->name, name, len))
+ {
+ if (strlen(sym->name) == len)
+ {
+ return sym;
+ }
+ if (bMatch)
+ {
+ bUnique = FALSE;
+ }
+ bMatch = TRUE;
+ if (sym->type == SYMBOL_METHOD)
+ {
+ match = sym;
+ }
+ }
+ sym = sym->next;
+ }
+ if (bExact)
+ {
+ return NULL;
+ }
+
+ if (!bUnique)
+ {
+ fprintf(stderr, "parse error: ambiguous symbol\n");
+ return NULL;
+ }
+ return match;
+}
+
+/*!
+ * \param[in] tab Symbol table to search.
+ * \param[in] type Type of symbol to find.
+ * \returns The first symbol in \p tab with type \p type,
+ * or NULL if there are no such symbols.
+ */
+gmx_sel_symrec_t *
+_gmx_sel_first_symbol(gmx_sel_symtab_t *tab, e_symbol_t type)
+{
+ gmx_sel_symrec_t *sym;
+
+ sym = tab->first;
+ while (sym)
+ {
+ if (sym->type == type)
+ {
+ return sym;
+ }
+ sym = sym->next;
+ }
+ return NULL;
+}
+
+/*!
+ * \param[in] after Start the search after this symbol.
+ * \param[in] type Type of symbol to find.
+ * \returns The next symbol after \p after with type \p type,
+ * or NULL if there are no more symbols.
+ */
+gmx_sel_symrec_t *
+_gmx_sel_next_symbol(gmx_sel_symrec_t *after, e_symbol_t type)
+{
+ gmx_sel_symrec_t *sym;
+
+ sym = after->next;
+ while (sym)
+ {
+ if (sym->type == type)
+ {
+ return sym;
+ }
+ sym = sym->next;
+ }
+ return NULL;
+}
+
+/*! \brief
+ * Internal utility function used in adding symbols to a symbol table.
+ *
+ * \param[in,out] tab Symbol table to add the symbol to.
+ * \param[in] name Name of the symbol to add.
+ * \param[out] ctype On error, the type of the conflicting symbol is
+ * written to \p *ctype.
+ * \returns Pointer to the new symbol record, or NULL if \p name
+ * conflicts with an existing symbol.
+ */
+static gmx_sel_symrec_t *
+add_symbol(gmx_sel_symtab_t *tab, const char *name, e_symbol_t *ctype)
+{
+ gmx_sel_symrec_t *sym, *psym;
+ int len;
+
+ /* Check if there is a conflicting symbol */
+ psym = NULL;
+ sym = tab->first;
+ while (sym)
+ {
+ if (!gmx_strcasecmp(sym->name, name))
+ {
+ *ctype = sym->type;
+ return NULL;
+ }
+ psym = sym;
+ sym = sym->next;
+ }
+
+ /* Create a new symbol record */
+ if (psym == NULL)
+ {
+ snew(tab->first, 1);
+ sym = tab->first;
+ }
+ else
+ {
+ snew(psym->next, 1);
+ sym = psym->next;
+ }
+ sym->name = strdup(name);
+ return sym;
+}
+
+/*!
+ * \param[in,out] tab Symbol table to add the symbol to.
+ * \param[in] name Name of the new symbol.
+ * \param[in] sel Value of the variable.
+ * \returns Pointer to the created symbol record, or NULL if there was a
+ * symbol with the same name.
+ */
+gmx_sel_symrec_t *
+_gmx_sel_add_var_symbol(gmx_sel_symtab_t *tab, const char *name,
+ struct t_selelem *sel)
+{
+ gmx_sel_symrec_t *sym;
+ e_symbol_t ctype;
+
+ sym = add_symbol(tab, name, &ctype);
+ if (!sym)
+ {
+ fprintf(stderr, "parse error: ");
+ switch (ctype)
+ {
+ case SYMBOL_RESERVED:
+ case SYMBOL_POS:
+ fprintf(stderr, "variable name (%s) conflicts with a reserved keyword\n",
+ name);
+ break;
+ case SYMBOL_VARIABLE:
+ fprintf(stderr, "duplicate variable name (%s)\n", name);
+ break;
+ case SYMBOL_METHOD:
+ fprintf(stderr, "variable name (%s) conflicts with a selection keyword\n",
+ name);
+ break;
+ }
+ return NULL;
+ }
+
+ sym->type = SYMBOL_VARIABLE;
+ sym->u.var = sel;
+ sel->refcount++;
+ return sym;
+}
+
+/*!
+ * \param[in,out] tab Symbol table to add the symbol to.
+ * \param[in] name Name of the new symbol.
+ * \param[in] method Method that this symbol represents.
+ * \returns Pointer to the created symbol record, or NULL if there was a
+ * symbol with the same name.
+ */
+gmx_sel_symrec_t *
+_gmx_sel_add_method_symbol(gmx_sel_symtab_t *tab, const char *name,
+ struct gmx_ana_selmethod_t *method)
+{
+ gmx_sel_symrec_t *sym;
+ e_symbol_t ctype;
+
+ sym = add_symbol(tab, name, &ctype);
+ if (!sym)
+ {
+ fprintf(stderr, "parse error: ");
+ switch (ctype)
+ {
+ case SYMBOL_RESERVED:
+ case SYMBOL_POS:
+ fprintf(stderr, "method name (%s) conflicts with a reserved keyword\n",
+ name);
+ break;
+ case SYMBOL_VARIABLE:
+ fprintf(stderr, "method name (%s) conflicts with a variable name\n",
+ name);
+ break;
+ case SYMBOL_METHOD:
+ fprintf(stderr, "duplicate method name (%s)\n", name);
+ break;
+ }
+ return NULL;
+ }
+
+ sym->type = SYMBOL_METHOD;
+ sym->u.meth = method;
+ return sym;
+}
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Handling of selection parser symbol table.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_SYMREC_H
+#define SELECTION_SYMREC_H
+
+struct t_selelem;
+struct gmx_ana_selmethod_t;
+
+/** Defines the type of the symbol. */
+typedef enum
+{
+ SYMBOL_RESERVED, /**< The symbol is a reserved keyword. */
+ SYMBOL_VARIABLE, /**< The symbol is a variable. */
+ SYMBOL_METHOD, /**< The symbol is a selection method. */
+ SYMBOL_POS /**< The symbol is a position keyword. */
+} e_symbol_t;
+
+/** Symbol table for the selection parser. */
+typedef struct gmx_sel_symtab_t gmx_sel_symtab_t;
+/** Single symbol for the selection parser. */
+typedef struct gmx_sel_symrec_t gmx_sel_symrec_t;
+
+/** Returns the name of a symbol. */
+char *
+_gmx_sel_sym_name(gmx_sel_symrec_t *sym);
+/** Returns the type of a symbol. */
+e_symbol_t
+_gmx_sel_sym_type(gmx_sel_symrec_t *sym);
+/** Returns the method associated with a \ref SYMBOL_METHOD symbol. */
+struct gmx_ana_selmethod_t *
+_gmx_sel_sym_value_method(gmx_sel_symrec_t *sym);
+/** Returns the method associated with a \ref SYMBOL_VARIABLE symbol. */
+struct t_selelem *
+_gmx_sel_sym_value_var(gmx_sel_symrec_t *sym);
+
+/** Creates a new symbol table. */
+int
+_gmx_sel_symtab_create(gmx_sel_symtab_t **tabp);
+/** Frees all memory allocated for a symbol table. */
+void
+_gmx_sel_symtab_free(gmx_sel_symtab_t *tab);
+/** Finds a symbol by name. */
+gmx_sel_symrec_t *
+_gmx_sel_find_symbol(gmx_sel_symtab_t *tab, const char *name, gmx_bool bExact);
+/** Finds a symbol by name. */
+gmx_sel_symrec_t *
+_gmx_sel_find_symbol_len(gmx_sel_symtab_t *tab, const char *name, size_t len,
+ gmx_bool bExact);
+/** Returns the first symbol of a given type. */
+gmx_sel_symrec_t *
+_gmx_sel_first_symbol(gmx_sel_symtab_t *tab, e_symbol_t type);
+/** Returns the next symbol of a given type. */
+gmx_sel_symrec_t *
+_gmx_sel_next_symbol(gmx_sel_symrec_t *after, e_symbol_t type);
+/** Adds a new variable symbol. */
+gmx_sel_symrec_t *
+_gmx_sel_add_var_symbol(gmx_sel_symtab_t *tab, const char *name,
+ struct t_selelem *sel);
+/** Adds a new method symbol. */
+gmx_sel_symrec_t *
+_gmx_sel_add_method_symbol(gmx_sel_symtab_t *tab, const char *name,
+ struct gmx_ana_selmethod_t *method);
+
+#endif
--- /dev/null
+IF (GTEST_FOUND)
+ include_directories(${GTEST_INCLUDE_DIRS})
+ add_executable(selection-test
+ selectioncollection.cpp selectionoption.cpp
+ test_main.cpp)
+ target_link_libraries(selection-test libgromacs ${GTEST_LIBRARIES})
+ add_test(SelectionUnitTests selection-test)
+ENDIF (GTEST_FOUND)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests selection parsing and compilation.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "smalloc.h"
+#include "statutil.h"
+#include "tpxio.h"
+#include "vec.h"
+
+#include "gromacs/errorreporting/emptyerrorreporter.h"
+#include "gromacs/selection/poscalc.h"
+#include "gromacs/selection/selectioncollection.h"
+#include "gromacs/selection/selection.h"
+
+namespace
+{
+
+/********************************************************************
+ * Test fixture for selection testing
+ */
+
+class SelectionCollectionTest : public ::testing::Test
+{
+ public:
+ SelectionCollectionTest();
+ ~SelectionCollectionTest();
+
+ void setAtomCount(int natoms)
+ {
+ _sc.setTopology(NULL, natoms);
+ }
+ void loadTopology(const char *filename);
+
+ gmx::SelectionCollection _sc;
+ gmx::EmptyErrorReporter _errors;
+ t_topology *_top;
+ t_trxframe *_frame;
+};
+
+SelectionCollectionTest::SelectionCollectionTest()
+ : _sc(NULL), _top(NULL), _frame(NULL)
+{
+ _sc.init();
+ _sc.setReferencePosType("atom");
+ _sc.setOutputPosType("atom");
+}
+
+
+SelectionCollectionTest::~SelectionCollectionTest()
+{
+ if (_top != NULL)
+ {
+ done_top(_top);
+ sfree(_top);
+ }
+
+ if (_frame != NULL)
+ {
+ sfree(_frame->x);
+ sfree(_frame);
+ }
+}
+
+
+void
+SelectionCollectionTest::loadTopology(const char *filename)
+{
+ char title[STRLEN];
+ int ePBC;
+ rvec *xtop;
+ matrix box;
+
+ snew(_top, 1);
+ read_tps_conf(filename, title, _top, &ePBC, &xtop, NULL, box, FALSE);
+
+ snew(_frame, 1);
+ _frame->flags = TRX_NEED_X;
+ _frame->natoms = _top->atoms.nr;
+ _frame->bX = TRUE;
+ snew(_frame->x, _frame->natoms);
+ memcpy(_frame->x, xtop, sizeof(*_frame->x) * _frame->natoms);
+ _frame->bBox = TRUE;
+ copy_mat(box, _frame->box);
+
+ ASSERT_EQ(0, _sc.setTopology(_top, -1));
+}
+
+
+/********************************************************************
+ * Tests for SelectionCollection functionality
+ */
+
+TEST_F(SelectionCollectionTest, HandlesNoSelections)
+{
+ EXPECT_FALSE(_sc.requiresTopology());
+ EXPECT_EQ(0, _sc.compile());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesSimpleSelections)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5", &_errors, &sel));
+ EXPECT_EQ(1U, sel.size());
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 2 to 4", &_errors, &sel));
+ EXPECT_EQ(2U, sel.size());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ ASSERT_EQ(2U, sel.size());
+ EXPECT_EQ(5, sel[0]->posCount());
+ EXPECT_EQ(3, sel[1]->posCount());
+}
+
+
+/********************************************************************
+ * Tests for selection syntax
+ */
+
+TEST_F(SelectionCollectionTest, HandlesConstantPositions)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("[1, -2, 3.5]", &_errors, &sel));
+ ASSERT_EQ(1U, sel.size());
+ EXPECT_FALSE(sel[0]->isDynamic());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(1, sel[0]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesStringMatching)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("resname RA RD", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("resname \"R[BD]\"", &_errors, &sel));
+ ASSERT_EQ(2U, sel.size());
+ EXPECT_FALSE(sel[0]->isDynamic());
+ EXPECT_FALSE(sel[1]->isDynamic());
+ loadTopology(SOURCE_DIR "/src/gromacs/selection/tests/simple.gro");
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(9, sel[0]->posCount());
+ EXPECT_EQ(6, sel[1]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesComparison)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("atomnr <= 5", &_errors, &sel));
+ ASSERT_EQ(1U, sel.size());
+ EXPECT_FALSE(sel[0]->isDynamic());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(5, sel[0]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesBasicBoolean)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 and atomnr 2 to 7", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 or not atomnr 3 to 8", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 and atomnr 2 to 6 and not not atomnr 3 to 7", &_errors, &sel));
+ ASSERT_EQ(3U, sel.size());
+ EXPECT_FALSE(sel[0]->isDynamic());
+ EXPECT_FALSE(sel[1]->isDynamic());
+ EXPECT_FALSE(sel[2]->isDynamic());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(4, sel[0]->posCount());
+ EXPECT_EQ(7, sel[1]->posCount());
+ EXPECT_EQ(3, sel[2]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesBooleanStaticAnalysis)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 and atomnr 2 to 7 and x < 2", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 and (atomnr 4 to 7 or x < 2)", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 and y < 3 and (atomnr 4 to 7 or x < 2)", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 5 and not (atomnr 4 to 7 or x < 2)", &_errors, &sel));
+ ASSERT_EQ(4U, sel.size());
+ EXPECT_TRUE(sel[0]->isDynamic());
+ EXPECT_TRUE(sel[1]->isDynamic());
+ EXPECT_TRUE(sel[2]->isDynamic());
+ EXPECT_TRUE(sel[3]->isDynamic());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(4, sel[0]->posCount());
+ EXPECT_EQ(5, sel[1]->posCount());
+ EXPECT_EQ(5, sel[2]->posCount());
+ EXPECT_EQ(3, sel[3]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesBooleanStaticAnalysisWithVariables)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("foo = atomnr 4 to 7 or x < 2", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 4 and foo", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 2 to 6 and y < 3 and foo", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 6 to 10 and not foo", &_errors, &sel));
+ ASSERT_EQ(3U, sel.size());
+ EXPECT_TRUE(sel[0]->isDynamic());
+ EXPECT_TRUE(sel[1]->isDynamic());
+ EXPECT_TRUE(sel[2]->isDynamic());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(4, sel[0]->posCount());
+ EXPECT_EQ(5, sel[1]->posCount());
+ EXPECT_EQ(3, sel[2]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesBooleanStaticAnalysisWithMoreVariables)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("foo = atomnr 4 to 7", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("bar = foo and x < 2", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("bar2 = foo and y < 2", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 1 to 4 and bar", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 2 to 6 and y < 3 and bar2", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("atomnr 6 to 10 and not foo", &_errors, &sel));
+ ASSERT_EQ(3U, sel.size());
+ EXPECT_TRUE(sel[0]->isDynamic());
+ EXPECT_TRUE(sel[1]->isDynamic());
+ EXPECT_FALSE(sel[2]->isDynamic());
+ setAtomCount(10);
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(1, sel[0]->posCount());
+ EXPECT_EQ(3, sel[1]->posCount());
+ EXPECT_EQ(3, sel[2]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesSameResidue)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("same residue as atomnr 1 4 12", &_errors, &sel));
+ ASSERT_EQ(1U, sel.size());
+ EXPECT_FALSE(sel[0]->isDynamic());
+ loadTopology(SOURCE_DIR "/src/gromacs/selection/tests/simple.gro");
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(9, sel[0]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesSameResidueName)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("same resname as atomnr 1 14", &_errors, &sel));
+ ASSERT_EQ(1U, sel.size());
+ EXPECT_FALSE(sel[0]->isDynamic());
+ loadTopology(SOURCE_DIR "/src/gromacs/selection/tests/simple.gro");
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(9, sel[0]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesArithmeticExpressions)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("x+1 > 3", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("(y-1)^2 <= 1", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("x+--1 > 3", &_errors, &sel));
+ EXPECT_EQ(0, _sc.parseFromString("-x+-1 < -3", &_errors, &sel));
+ ASSERT_EQ(4U, sel.size());
+ EXPECT_TRUE(sel[0]->isDynamic());
+ EXPECT_TRUE(sel[1]->isDynamic());
+ EXPECT_TRUE(sel[2]->isDynamic());
+ EXPECT_TRUE(sel[3]->isDynamic());
+ loadTopology(SOURCE_DIR "/src/gromacs/selection/tests/simple.gro");
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(15, sel[0]->posCount());
+ EXPECT_EQ(15, sel[1]->posCount());
+ EXPECT_EQ(15, sel[2]->posCount());
+ EXPECT_EQ(15, sel[3]->posCount());
+ EXPECT_EQ(0, _sc.evaluate(_frame, NULL));
+ EXPECT_EQ(7, sel[0]->posCount());
+ EXPECT_EQ(8, sel[1]->posCount());
+ EXPECT_EQ(7, sel[2]->posCount());
+ EXPECT_EQ(7, sel[3]->posCount());
+ EXPECT_EQ(0, _sc.evaluateFinal(1));
+ EXPECT_EQ(15, sel[0]->posCount());
+ EXPECT_EQ(15, sel[1]->posCount());
+ EXPECT_EQ(15, sel[2]->posCount());
+ EXPECT_EQ(15, sel[3]->posCount());
+}
+
+
+TEST_F(SelectionCollectionTest, HandlesWithinConstantPositions)
+{
+ std::vector<gmx::Selection *> sel;
+ EXPECT_EQ(0, _sc.parseFromString("within 1 of [2, 1, 0]", &_errors, &sel));
+ ASSERT_EQ(1U, sel.size());
+ EXPECT_TRUE(sel[0]->isDynamic());
+ loadTopology(SOURCE_DIR "/src/gromacs/selection/tests/simple.gro");
+ EXPECT_EQ(0, _sc.compile());
+ EXPECT_EQ(15, sel[0]->posCount());
+ EXPECT_EQ(0, _sc.evaluate(_frame, NULL));
+ EXPECT_EQ(4, sel[0]->posCount());
+ EXPECT_EQ(0, _sc.evaluateFinal(1));
+ EXPECT_EQ(15, sel[0]->posCount());
+}
+
+} // namespace
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests selection parsing and compilation.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_selection
+ */
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/errorreporting/emptyerrorreporter.h"
+#include "gromacs/options/globalproperties.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionsassigner.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectioncollection.h"
+#include "gromacs/selection/selectionoption.h"
+
+namespace
+{
+
+class SelectionOptionTest : public ::testing::Test
+{
+ public:
+ SelectionOptionTest();
+
+ gmx::SelectionCollection _sc;
+ gmx::Options _options;
+};
+
+SelectionOptionTest::SelectionOptionTest()
+ : _sc(NULL), _options(NULL, NULL)
+{
+ _sc.init();
+ _sc.setReferencePosType("atom");
+ _sc.setOutputPosType("atom");
+ _options.globalProperties().setSelectionCollection(&_sc);
+}
+
+
+TEST_F(SelectionOptionTest, ParsesSimpleSelection)
+{
+ gmx::Selection *sel = NULL;
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(&sel));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ ASSERT_EQ(0, assigner.startOption("sel"));
+ EXPECT_EQ(0, assigner.appendValue("resname RA RB"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+ ASSERT_TRUE(sel != NULL);
+ ASSERT_FALSE(sel->isDynamic());
+}
+
+
+TEST_F(SelectionOptionTest, HandlesDynamicSelectionWhenStaticRequired)
+{
+ gmx::Selection *sel = NULL;
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(&sel).onlyStatic());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ ASSERT_EQ(0, assigner.startOption("sel"));
+ EXPECT_NE(0, assigner.appendValue("resname RA RB and x < 5"));
+ EXPECT_NE(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+}
+
+
+TEST_F(SelectionOptionTest, HandlesTooManySelections)
+{
+ gmx::Selection *sel = NULL;
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(&sel));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ ASSERT_EQ(0, assigner.startOption("sel"));
+ EXPECT_EQ(0, assigner.appendValue("resname RA RB"));
+ EXPECT_NE(0, assigner.appendValue("resname RB RC"));
+ EXPECT_NE(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+ ASSERT_TRUE(sel != NULL);
+}
+
+
+TEST_F(SelectionOptionTest, HandlesTooFewSelections)
+{
+ gmx::Selection *sel[2] = {NULL, NULL};
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(sel).valueCount(2));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ ASSERT_EQ(0, assigner.startOption("sel"));
+ EXPECT_EQ(0, assigner.appendValue("resname RA RB"));
+ EXPECT_NE(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+}
+
+
+TEST_F(SelectionOptionTest, HandlesDelayedRequiredSelection)
+{
+ gmx::Selection *sel = NULL;
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(&sel).required());
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+ EXPECT_EQ(0, _sc.parseRequestedFromString("resname RA RB", &errors));
+ ASSERT_TRUE(sel != NULL);
+}
+
+
+TEST_F(SelectionOptionTest, HandlesTooFewDelayedRequiredSelections)
+{
+ gmx::Selection *sel[2] = {NULL, NULL};
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(sel).required()
+ .valueCount(2));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+ EXPECT_NE(0, _sc.parseRequestedFromString("resname RA RB", &errors));
+}
+
+
+TEST_F(SelectionOptionTest, HandlesDelayedOptionalSelection)
+{
+ gmx::Selection *sel = NULL;
+ using gmx::SelectionOption;
+ _options.addOption(SelectionOption("sel").store(&sel));
+
+ gmx::EmptyErrorReporter errors;
+ gmx::OptionsAssigner assigner(&_options, &errors);
+ ASSERT_EQ(0, assigner.startOption("sel"));
+ EXPECT_EQ(0, assigner.finish());
+ EXPECT_EQ(0, _options.finish(&errors));
+ EXPECT_EQ(0, _sc.parseRequestedFromString("resname RA RB", &errors));
+ ASSERT_TRUE(sel != NULL);
+}
+
+} // namespace
--- /dev/null
+Test system
+ 15
+ 1RA CB 1 1.000 1.000 0.000
+ 1RA S1 2 1.000 2.000 0.000
+ 1RA S2 3 1.000 3.000 0.000
+ 2RB CB 4 1.000 4.000 0.000
+ 2RB S1 5 2.000 1.000 0.000
+ 2RB S2 6 2.000 2.000 0.000
+ 3RA CB 7 2.000 3.000 0.000
+ 3RA S1 8 2.000 4.000 0.000
+ 3RA S2 9 3.000 1.000 0.000
+ 4RC CB 10 3.000 2.000 0.000
+ 4RC S1 11 3.000 3.000 0.000
+ 4RC S2 12 3.000 4.000 0.000
+ 5RD CB 13 4.000 1.000 0.000
+ 5RD S1 14 4.000 2.000 0.000
+ 5RD S2 15 4.000 3.000 0.000
+ 10.00000 10.00000 10.00000
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * main() for unit tests that use Google C++ Testing Framework.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/fatalerror/fatalerror.h"
+
+/*! \brief
+ * Initializes unit testing with Google C++ Testing Framework.
+ */
+int main(int argc, char *argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ ::gmx::setFatalErrorHandler(NULL);
+ return RUN_ALL_TESTS();
+}
--- /dev/null
+file(GLOB TRAJECTORYANALYSIS_SOURCES *.cpp modules/*.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${TRAJECTORYANALYSIS_SOURCES} PARENT_SCOPE)
+
+set(TRAJECTORYANALYSIS_PUBLIC_HEADERS
+ analysismodule.h
+ analysissettings.h
+ cmdlinerunner.h)
+install(FILES ${TRAJECTORYANALYSIS_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/trajectoryanalysis
+ COMPONENT development)
+
+if (BUILD_TESTING)
+ add_subdirectory(tests)
+endif (BUILD_TESTING)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation classes for gmx::TrajectoryAnalysisModule
+ * and gmx::TrajectoryAnalysisModuleData.
+ *
+ * \ingroup module_trajectoryanalysis
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISMODULE_IMPL_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISMODULE_IMPL_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "analysismodule.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisData;
+class AnalysisDataHandle;
+
+class TrajectoryAnalysisModuleData::Impl
+{
+ public:
+ typedef std::map<std::string, AnalysisDataHandle *> HandleContainer;
+
+ Impl() : _selections(NULL) {}
+ ~Impl();
+
+ int finishHandles();
+
+ HandleContainer _handles;
+ const SelectionCollection *_selections;
+};
+
+class TrajectoryAnalysisModule::Impl
+{
+ public:
+ typedef std::map<std::string, AbstractAnalysisData *> DatasetContainer;
+ typedef std::map<std::string, AnalysisData *> AnalysisDatasetContainer;
+
+ std::vector<std::string> _datasetNames;
+ DatasetContainer _datasets;
+ AnalysisDatasetContainer _analysisDatasets;
+};
+
+/*! \internal \brief
+ * Basic thread-local trajectory analysis data storage class.
+ *
+ * Most simple tools should only require data handles and selections to be
+ * thread-local, so this class implements just that.
+ */
+class TrajectoryAnalysisModuleDataBasic : public TrajectoryAnalysisModuleData
+{
+ public:
+ virtual int finish();
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in analysismodule.h
+ *
+ * \ingroup module_trajectoryanalysis
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+#include <cassert>
+
+#include "gromacs/analysisdata/analysisdata.h"
+
+#include "analysismodule-impl.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * TrajectoryAnalysisModuleData::Impl
+ */
+
+TrajectoryAnalysisModuleData::Impl::~Impl()
+{
+ finishHandles();
+}
+
+
+int TrajectoryAnalysisModuleData::Impl::finishHandles()
+{
+ int rc = 0;
+ HandleContainer::const_iterator i;
+ for (i = _handles.begin(); i != _handles.end(); ++i)
+ {
+ int rc1 = i->second->finishData();
+ rc = (rc == 0 ? rc1 : rc);
+ }
+ _handles.clear();
+ return rc;
+}
+
+
+/********************************************************************
+ * TrajectoryAnalysisModuleData
+ */
+
+TrajectoryAnalysisModuleData::TrajectoryAnalysisModuleData()
+ : _impl(new Impl)
+{
+}
+
+
+TrajectoryAnalysisModuleData::~TrajectoryAnalysisModuleData()
+{
+ delete _impl;
+}
+
+
+int TrajectoryAnalysisModuleData::init(TrajectoryAnalysisModule *module,
+ AnalysisDataParallelOptions opt,
+ const SelectionCollection &selections)
+{
+ TrajectoryAnalysisModule::Impl::AnalysisDatasetContainer::const_iterator i;
+ for (i = module->_impl->_analysisDatasets.begin();
+ i != module->_impl->_analysisDatasets.end(); ++i)
+ {
+ AnalysisDataHandle *handle = NULL;
+ int rc = i->second->startData(&handle, opt);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ _impl->_handles[i->first] = handle;
+ }
+ _impl->_selections = &selections;
+ return 0;
+}
+
+
+int TrajectoryAnalysisModuleData::finishDataHandles()
+{
+ return _impl->finishHandles();
+}
+
+
+AnalysisDataHandle *TrajectoryAnalysisModuleData::dataHandle(const char *name)
+{
+ Impl::HandleContainer::const_iterator i = _impl->_handles.find(name);
+ assert(i != _impl->_handles.end() || !"Data handle requested on unknown dataset");
+ return (i != _impl->_handles.end()) ? (*i).second : NULL;
+}
+
+
+Selection *TrajectoryAnalysisModuleData::parallelSelection(Selection *selection)
+{
+ // TODO: Implement properly.
+ return selection;
+}
+
+
+std::vector<Selection *>
+TrajectoryAnalysisModuleData::parallelSelections(const std::vector<Selection *> &selections)
+{
+ std::vector<Selection *> newSelections;
+ newSelections.reserve(selections.size());
+ std::vector<Selection *>::const_iterator i = selections.begin();
+ for ( ; i != selections.end(); ++i)
+ {
+ newSelections.push_back(parallelSelection(*i));
+ }
+ return newSelections;
+}
+
+
+/********************************************************************
+ * TrajectoryAnalysisModuleDataBasic
+ */
+
+int
+TrajectoryAnalysisModuleDataBasic::finish()
+{
+ return finishDataHandles();
+}
+
+
+/********************************************************************
+ * TrajectoryAnalysisModule
+ */
+
+TrajectoryAnalysisModule::TrajectoryAnalysisModule()
+ : _impl(new Impl)
+{
+}
+
+
+TrajectoryAnalysisModule::~TrajectoryAnalysisModule()
+{
+ delete _impl;
+}
+
+
+int TrajectoryAnalysisModule::initOptionsDone(TrajectoryAnalysisSettings * /*settings*/)
+{
+ return 0;
+}
+
+
+int TrajectoryAnalysisModule::initAfterFirstFrame(const t_trxframe &/*fr*/)
+{
+ return 0;
+}
+
+
+int TrajectoryAnalysisModule::startFrames(AnalysisDataParallelOptions opt,
+ const SelectionCollection &selections,
+ TrajectoryAnalysisModuleData **pdatap)
+{
+ TrajectoryAnalysisModuleDataBasic *pdata
+ = new TrajectoryAnalysisModuleDataBasic();
+ *pdatap = pdata;
+ int rc = pdata->init(this, opt, selections);
+ if (rc != 0)
+ {
+ delete pdata;
+ *pdatap = NULL;
+ }
+ return rc;
+}
+
+
+int TrajectoryAnalysisModule::finishFrames(TrajectoryAnalysisModuleData * /*pdata*/)
+{
+ return 0;
+}
+
+
+int TrajectoryAnalysisModule::datasetCount() const
+{
+ return _impl->_datasetNames.size();
+}
+
+
+const std::vector<std::string> &TrajectoryAnalysisModule::datasetNames() const
+{
+ return _impl->_datasetNames;
+}
+
+
+AbstractAnalysisData *TrajectoryAnalysisModule::datasetFromIndex(int index) const
+{
+ if (index < 0 || index >= datasetCount())
+ {
+ return NULL;
+ }
+ return _impl->_datasets[_impl->_datasetNames[index]];
+}
+
+
+AbstractAnalysisData *TrajectoryAnalysisModule::datasetFromName(const char *name) const
+{
+ Impl::DatasetContainer::const_iterator item = _impl->_datasets.find(name);
+ if (item == _impl->_datasets.end())
+ {
+ return NULL;
+ }
+ return item->second;
+}
+
+
+void TrajectoryAnalysisModule::registerBasicDataset(AbstractAnalysisData *data,
+ const char *name)
+{
+ // TODO: Check for duplicates
+ _impl->_datasets[name] = data;
+ _impl->_datasetNames.push_back(name);
+}
+
+
+void TrajectoryAnalysisModule::registerAnalysisDataset(AnalysisData *data,
+ const char *name)
+{
+ registerBasicDataset(data, name);
+ _impl->_analysisDatasets[name] = data;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declaration of TrajanaModule and integrally related classes.
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISMODULE_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISMODULE_H
+
+#include <string>
+#include <vector>
+
+#include "../legacyheaders/typedefs.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisData;
+class AnalysisDataHandle;
+class Options;
+class Selection;
+class SelectionCollection;
+class TopologyInformation;
+class TrajectoryAnalysisModule;
+class TrajectoryAnalysisSettings;
+
+/*! \brief
+ * Base class for thread-local data storage during trajectory analysis.
+ *
+ * Thread-local storage of data handles and selections is implemented in this
+ * class; TrajectoryAnalysisModule instances can access the thread-local values
+ * using dataHandle() and parallelSelection().
+ *
+ * \see TrajectoryAnalysisModule::startFrames()
+ */
+class TrajectoryAnalysisModuleData
+{
+ public:
+ virtual ~TrajectoryAnalysisModuleData();
+
+ /*! \brief
+ * Initializes thread-local storage for data handles and selections.
+ *
+ * \param[in] module Analysis module to use for data objects.
+ * \param[in] opt Data parallelization options.
+ * \param[in] selections Thread-local selection collection.
+ *
+ * Calls AnalysisData::startData() on all data objects registered with
+ * TrajectoryAnalysisModule::registerAnalysisDataset() in \p module.
+ * The handles are accessible through dataHandle().
+ */
+ int init(TrajectoryAnalysisModule *module, /*AnalysisDataParallelOptions*/ void* opt,
+ const SelectionCollection &selections);
+
+ /*! \brief
+ * Performs any finishing actions after all frames have been processed.
+ *
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * This function is called immediately before the destructor.
+ * All implementations should call finishDataHandles().
+ */
+ virtual int finish() = 0;
+
+ /*! \brief
+ * Returns a data handle for a dataset with a given name.
+ *
+ * Allowed names are those that have been registered with
+ * TrajectoryAnalysisModule::registerAnalysisData().
+ */
+ AnalysisDataHandle *dataHandle(const char *name);
+ /*! \brief
+ * Returns a selection that corresponds to the given selection.
+ */
+ Selection *parallelSelection(Selection *selection);
+ /*! \brief
+ * Returns a set of selection that corresponds to the given selections.
+ */
+ std::vector<Selection *> parallelSelections(const std::vector<Selection *> &selections);
+
+ protected:
+ //! Initializes data.
+ TrajectoryAnalysisModuleData();
+
+ /*! \brief
+ * Calls finishData() on all data handles.
+ *
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * This function should be called from the implementation of finish()
+ * in all subclasses.
+ */
+ int finishDataHandles();
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ TrajectoryAnalysisModuleData(const TrajectoryAnalysisModuleData &);
+ void operator =(const TrajectoryAnalysisModuleData &);
+};
+
+
+/*! \brief
+ * Trajectory analysis method.
+ *
+ * For parallel analysis using threads, only a single object is constructed,
+ * but the methods startFrames(), analyzeFrame() and finishFrames() are
+ * called in each thread. Frame-local data should be initialized in
+ * startFrames() and stored in a class derived from
+ * TrajectoryAnalysisModuleData that is passed to the other methods.
+ */
+class TrajectoryAnalysisModule
+{
+ public:
+ virtual ~TrajectoryAnalysisModule();
+
+ /*! \brief
+ * Initializes parameters understood by the module.
+ *
+ * In addition to initializing the accepted parameters, this function
+ * should also set any required options using the options() object,
+ * see TrajanaOptions for more details.
+ * It can also customize the acceptable selections using selections(),
+ * see Selections.
+ *
+ * If option values depend on the parameter values provided by the
+ * user, see initParamsDone().
+ */
+ virtual Options *initOptions(TrajectoryAnalysisSettings *settings) = 0;
+ /*! \brief
+ * Called after all parameter values have been set.
+ *
+ * If the module needs to set options that affect topology loading or
+ * selection initialization based on parameters values, this function
+ * has to be overridden.
+ *
+ * The default implementation does nothing.
+ */
+ virtual int initOptionsDone(TrajectoryAnalysisSettings *settings);
+ /*! \brief
+ * Initializes the analysis.
+ *
+ * \returns Zero on success, a non-zero error code on error.
+ *
+ * When this function is called, selections have been initialized based
+ * on user input, and a topology has been loaded if provided by the
+ * user. For dynamic selections, the selections have been evaluated to
+ * the largest possible selection, i.e., the selections passed to
+ * analyzeFrame() are always a subset of the selections provided here.
+ */
+ virtual int initAnalysis(const TopologyInformation &top) = 0;
+ /*! \brief
+ * Performs additional initialization after reading the first frame.
+ *
+ * \returns Zero on success, a non-zero error code on error.
+ *
+ * When this function is called, selections are the same as in
+ * initAnalysis(), i.e., they have not been evaluated for the first
+ * frame.
+ *
+ * It is necessary to override this method only if the module needs to
+ * do initialization for which it requires data from the first frame.
+ *
+ * The default implementation does nothing.
+ */
+ virtual int initAfterFirstFrame(const t_trxframe &fr);
+
+ /*! \brief
+ * Starts the analysis of frames.
+ *
+ * \param[in] opt
+ * \param[in] selections Frame-local selection object.
+ * \param[out] pdatap Data structure for thread-local data.
+ *
+ * This function is necessary only for threaded parallelization.
+ * It is called once for each thread and should initialize a class that
+ * contains any required frame-local data in \p *pdatap.
+ * The default implementation creates a basic data structure that holds
+ * thread-local data handles for all data objects registered with
+ * registerAnalysisDataset(), as well as the thread-local selection
+ * collection. These can be accessed in analyzeFrame() using the
+ * methods in TrajectoryAnalysisModuleData.
+ * If other thread-local data is needed, this function should be
+ * overridden and it should create an instance of a class derived from
+ * TrajectoryAnalysisModuleData.
+ *
+ * \see TrajectoryAnalysisModuleData
+ */
+ virtual int startFrames(/*AnalysisDataParallelOptions*/ void* opt,
+ const SelectionCollection &selections,
+ TrajectoryAnalysisModuleData **pdatap);
+ /*! \brief
+ * Analyzes a single frame.
+ *
+ * \param[in] frnr Frame number, a zero-based index that
+ * uniquely identifies the frame.
+ * \param[in] fr Current frame.
+ * \param[in] pbc Periodic boundary conditions for \p fr.
+ * \param[in,out] pdata Data structure for frame-local data.
+ * \return 0 on success, a non-zero error code or error.
+ *
+ * This function is called once for each frame to be analyzed,
+ * and should analyze the positions provided in \p sel.
+ *
+ * For threaded analysis, this function is called asynchronously in
+ * different threads to analyze different frames. The \p pdata
+ * structure is one of the structures created with startFrames(),
+ * but no assumptions should be made about which of these data
+ * structures is used. It is guaranteed that two instances of
+ * analyzeFrame() are not running concurrently with the same \p pdata
+ * data structure.
+ * Any access to data structures not stored in \p pdata should be
+ * designed to be thread-safe.
+ */
+ virtual int analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+ TrajectoryAnalysisModuleData *pdata) = 0;
+ /*! \brief
+ * Finishes the analysis of frames.
+ *
+ * \param[in] pdata Data structure for thread-local data.
+ *
+ * This function is called once for each call of startFrames(),
+ * with the data structure returned by the corresponding startFrames().
+ * The \p pdata object should be destroyed by the caller after this
+ * function has been called.
+ *
+ * You only need to override this method if you need custom
+ * operations to combine data from the frame-local data structures
+ * to get the final result. In such cases, the data should be
+ * aggregated in this function and stored in a member attribute.
+ *
+ * The default implementation does nothing.
+ *
+ * \see startFrames()
+ */
+ virtual int finishFrames(TrajectoryAnalysisModuleData *pdata);
+
+ /*! \brief
+ * Postprocesses data after frames have been read.
+ *
+ * This function is called after all finishFrames() calls have been
+ * called.
+ */
+ virtual int finishAnalysis(int nframes) = 0;
+ /*! \brief
+ * Writes output into files and/or standard output/error.
+ *
+ * All output from the module, excluding data written out for each
+ * frame during analyzeFrame(), should be confined into this function.
+ * This function is guaranteed to be called only after
+ * finishAnalysis().
+ */
+ virtual int writeOutput() = 0;
+
+ /*! \brief
+ * Returns the number of datasets provided by the module.
+ */
+ int datasetCount() const;
+ /*! \brief
+ * Returns a vector with the names of the datasets.
+ */
+ const std::vector<std::string> &datasetNames() const;
+ /*! \brief
+ * Returns a pointer to the data set \p index.
+ *
+ * \param[in] index Data set to query for.
+ * \returns A pointer to the data set, or NULL if \p index is not
+ * valid.
+ *
+ * The return value is not const to allow callers to add modules to the
+ * data sets. However, the AbstractAnalysisData interface does not
+ * provide any means to alter the data, so the module does not need to
+ * care about external modifications.
+ */
+ AbstractAnalysisData *datasetFromIndex(int index) const;
+ /*! \brief
+ * Returns a pointer to the data set with name \p name
+ *
+ * \param[in] name Data set to query for.
+ * \returns A pointer to the data set, or NULL if \p name is not
+ * recognized.
+ *
+ * The return value is not const to allow callers to add modules to the
+ * data sets. However, the AbstractAnalysisData interface does not
+ * provide any means to alter the data, so the module does not need to
+ * care about external modifications.
+ */
+ AbstractAnalysisData *datasetFromName(const char *name) const;
+
+ protected:
+ //! Initializes the dataset registration mechanism.
+ TrajectoryAnalysisModule();
+
+ /*! \brief
+ * Registers a dataset that exports data.
+ */
+ void registerBasicDataset(AbstractAnalysisData *data, const char *name);
+ /*! \brief
+ * Registers a parallelized dataset that exports data.
+ */
+ void registerAnalysisDataset(AnalysisData *data, const char *name);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ /*! \brief
+ * Needed to access the registered analysis data sets.
+ */
+ friend class TrajectoryAnalysisModuleData;
+
+ // Disallow copy and assign.
+ TrajectoryAnalysisModule(const TrajectoryAnalysisModule &);
+ void operator =(const TrajectoryAnalysisModule &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declaration of TrajanaModule and integrally related classes.
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_IMPL_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_IMPL_H
+
+#include "analysissettings.h"
+
+namespace gmx
+{
+
+class TrajectoryAnalysisSettings::Impl
+{
+ public:
+ Impl() : flags(0), frflags(0), bRmPBC(true), bPBC(true) {}
+
+ unsigned long flags;
+ int frflags;
+
+ bool bRmPBC;
+ bool bPBC;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements TrajectoryAnalysisSettings..
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <smalloc.h>
+#include <statutil.h>
+#include <vec.h>
+
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/trajectoryanalysis/analysissettings.h"
+
+#include "analysissettings-impl.h"
+
+namespace gmx
+{
+
+
+/********************************************************************
+ * TrajectoryAnalysisSettings
+ */
+
+TrajectoryAnalysisSettings::TrajectoryAnalysisSettings()
+ : _impl(new Impl)
+{
+ _impl->frflags |= TRX_NEED_X;
+}
+
+
+TrajectoryAnalysisSettings::~TrajectoryAnalysisSettings()
+{
+ delete _impl;
+}
+
+
+unsigned long
+TrajectoryAnalysisSettings::flags() const
+{
+ return _impl->flags;
+}
+
+
+bool
+TrajectoryAnalysisSettings::hasFlag(unsigned long flag) const
+{
+ return _impl->flags & flag;
+}
+
+
+bool
+TrajectoryAnalysisSettings::hasPBC() const
+{
+ return _impl->bPBC;
+}
+
+
+bool
+TrajectoryAnalysisSettings::hasRmPBC() const
+{
+ return _impl->bRmPBC;
+}
+
+
+int
+TrajectoryAnalysisSettings::frflags() const
+{
+ return _impl->frflags;
+}
+
+
+int
+TrajectoryAnalysisSettings::setFlags(unsigned long flags)
+{
+ _impl->flags = flags;
+ return 0;
+}
+
+
+int
+TrajectoryAnalysisSettings::setFlag(unsigned long flag, bool bSet)
+{
+ if (bSet)
+ {
+ _impl->flags |= flag;
+ }
+ else
+ {
+ _impl->flags &= ~flag;
+ }
+ return 0;
+}
+
+
+int
+TrajectoryAnalysisSettings::setPBC(bool bPBC)
+{
+ _impl->bPBC = bPBC;
+ return 0;
+}
+
+
+int
+TrajectoryAnalysisSettings::setRmPBC(bool bRmPBC)
+{
+ _impl->bRmPBC = bRmPBC;
+ return 0;
+}
+
+
+int
+TrajectoryAnalysisSettings::setFrameFlags(int frflags)
+{
+ _impl->frflags = frflags;
+ return 0;
+}
+
+
+/********************************************************************
+ * TopologyInformation
+ */
+
+TopologyInformation::TopologyInformation()
+ : _top(NULL), _bTop(false), _xtop(NULL), _ePBC(-1)
+{
+ clear_mat(_boxtop);
+}
+
+TopologyInformation::~TopologyInformation()
+{
+ if (_top)
+ {
+ done_top(_top);
+ sfree(_top);
+ }
+ sfree(_xtop);
+}
+
+int
+TopologyInformation::getTopologyConf(rvec **x, matrix box) const
+{
+ if (box)
+ {
+ copy_mat(const_cast<rvec *>(_boxtop), box);
+ }
+ if (x)
+ {
+ if (!_xtop)
+ {
+ *x = NULL;
+ GMX_ERROR(eeInvalidValue,
+ "Topology coordinates requested without setting efUseTopX");
+ return -1;
+ }
+ *x = _xtop;
+ }
+ return 0;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declaration of TrajanaModule and integrally related classes.
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_H
+
+#include "../legacyheaders/typedefs.h"
+
+namespace gmx
+{
+
+class Options;
+class TrajectoryAnalysisRunnerCommon;
+
+/*! \brief
+ * Trajectory analysis module configuration object.
+ *
+ * This class is used by trajectory analysis modules to inform the caller
+ * about the requirements they have on the input (e.g., whether a topology is
+ * required, or whether PBC removal makes sense). It is also used to pass
+ * similar information back to the analysis module after parsing user input.
+ *
+ * Having this functionality as a separate class makes the TrajanaModule
+ * interface much cleaner, and also reduces the need to change existing code
+ * when new options are added.
+ *
+ * The class name is not the best possible, as the class is also used to pass
+ * topology information into trajectory analysis modules.
+ */
+class TrajectoryAnalysisSettings
+{
+ public:
+ //! Recognized flags.
+ enum {
+ /*! \brief
+ * Forces loading of a topology file.
+ *
+ * If this flag is not specified, the topology file is loaded only
+ * if it is provided on the command line explicitly.
+ *
+ * \see topology(), hasFullTopology()
+ */
+ efRequireTop = 1<<0,
+ /*! \brief
+ * Requests topology coordinates.
+ *
+ * If this flag is specified, the coordinates loaded from the
+ * topology can be accessed with getTopologyConf().
+ *
+ * \see getTopologyConf()
+ */
+ efUseTopX = 1<<1,
+ /*! \brief
+ * Disallows the user from changing PBC handling.
+ *
+ * If this option is not specified, the analysis module (see
+ * TrajanaModule::analyzeFrame()) may be passed a NULL PBC
+ * structure, and it should be able to handle such a situation.
+ */
+ efNoUserPBC = 1<<4,
+ /*! \brief
+ * Disallows the user from changing PBC removal.
+ */
+ efNoUserRmPBC = 1<<5,
+ /*! \brief
+ * Requests dumps of parsed and compiled selection trees.
+ *
+ * This flag is used by internal debugging tools to request
+ * the selection trees dumping to stderr.
+ */
+ efDebugSelection = 1<<16,
+ };
+
+ TrajectoryAnalysisSettings();
+ ~TrajectoryAnalysisSettings();
+
+ //! Returns the currently set flags.
+ unsigned long flags() const;
+ bool hasFlag(unsigned long flag) const;
+ /*! \brief
+ * Returns whether PBC should be used.
+ *
+ * Returns the value set with setPBC(). TrajanaModule subcalees can
+ * access the user-provided value TrajanaModule::initParamsDone() (or
+ * at any point after that), as long as the value has not been replaced
+ * with another call to setPBC().
+ */
+ bool hasPBC() const;
+ /*! \brief
+ * Returns whether molecules should be made whole.
+ *
+ * See hasPBC() for information on accessing or overriding the
+ * user-provided value.
+ */
+ bool hasRmPBC() const;
+ //! Returns the currently set frame flags.
+ int frflags() const;
+
+ /*! \brief
+ * Sets flags.
+ *
+ * Overrides any earlier set flags.
+ * By default, no flags are set.
+ */
+ int setFlags(unsigned long flags);
+ //! Sets or clears an individual flag.
+ int setFlag(unsigned long flag, bool bSet = true);
+ /*! \brief
+ * Sets whether PBC are used.
+ *
+ * \param[in] bPBC TRUE if PBC should be used.
+ * \returns 0 on success.
+ *
+ * If called in TrajanaModule::initParams(), this function sets the
+ * default for whether PBC are used in the analysis.
+ * If \ref efNoUserPBC is not set, a command-line option is provided
+ * for the user to override the default value.
+ * If called later, it overrides the setting provided by the user or an
+ * earlier call.
+ *
+ * If this function is not called, the default is to use PBC.
+ *
+ * If PBC are not used, the \p pbc pointer passed to
+ * TrajanaModule::analyzeFrame() is NULL.
+ * The value of the flag can also be accessed with hasPBC().
+ *
+ * \see \ref efNoUserPBC
+ */
+ int setPBC(bool bPBC);
+ /*! \brief
+ * Sets whether molecules are made whole.
+ *
+ * \param[in] bRmPBC TRUE if molecules should be made whole.
+ * \returns 0 on success.
+ *
+ * If called in TrajanaModule::initParams(), this function sets the
+ * default for whether molecules are made whole.
+ * If \ref efNoUserRmPBC is not set, a command-line option is provided
+ * for the user to override the default value.
+ * If called later, it overrides the setting provided by the user or an
+ * earlier call.
+ *
+ * If this function is not called, the default is to make molecules
+ * whole.
+ *
+ * The main use of this function is to call it with \c false if your
+ * analysis program does not require whole molecules as this can
+ * increase the performance.
+ * In such a case, you can also specify \ref efNoUserRmPBC to not to
+ * confuse the user with an option that would only slow the program
+ * down.
+ *
+ * \see \ref efNoUserRmPBC
+ */
+ int setRmPBC(bool bRmPBC);
+ /*! \brief
+ * Sets flags that determine what to read from the trajectory.
+ *
+ * \param[in] frflags Flags for what to read from the trajectory file.
+ * \returns 0 on success, an error code on error.
+ *
+ * If this function is not called, the flags default to TRX_NEED_X.
+ * If the analysis module needs some other information (velocities,
+ * forces), it can call this function to load additional information
+ * from the trajectory.
+ */
+ int setFrameFlags(int frflags);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ TrajectoryAnalysisSettings(const TrajectoryAnalysisSettings &);
+ void operator =(const TrajectoryAnalysisSettings &);
+
+ friend class TrajectoryAnalysisRunnerCommon;
+};
+
+class TopologyInformation
+{
+ public:
+ //! Returns true if a topology file was loaded.
+ bool hasTopology() const { return _top != NULL; }
+ //! Returns true if a full topology file was loaded.
+ bool hasFullTopology() const { return _bTop; }
+ //! Returns the loaded topology, or NULL if not loaded.
+ t_topology *topology() const { return _top; }
+ //! Returns the ePBC field from the topology.
+ int ePBC() const { return _ePBC; }
+ /*! \brief
+ * Gets the configuration from the topology.
+ *
+ * \param[out] x Topology coordinate pointer to initialize.
+ * (can be NULL, in which case it is not used).
+ * \param[out] box Box size from the topology file
+ * (can be NULL, in which case it is not used).
+ * \returns 0 on success, a non-zero error code on error.
+ *
+ * If \ref efUseTopX has not been specified, the \p x parameter should
+ * be NULL.
+ *
+ * The pointer returned in \p *x should not be freed.
+ */
+ int getTopologyConf(rvec **x, matrix box) const;
+
+ private:
+ TopologyInformation();
+ ~TopologyInformation();
+
+ //! The topology structure, or \p NULL if no topology loaded.
+ t_topology *_top;
+ //! TRUE if full tpx file was loaded, FALSE otherwise.
+ bool _bTop;
+ //! Coordinates from the topology (can be NULL).
+ rvec *_xtop;
+ //! The box loaded from the topology file.
+ matrix _boxtop;
+ //! The ePBC field loaded from the topology file.
+ int _ePBC;
+
+ // Disallow copy and assign.
+ TopologyInformation(const TopologyInformation &);
+ void operator =(const TopologyInformation &);
+
+ friend class TrajectoryAnalysisRunnerCommon;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::TrajectoryAnalysisCommandLineRunner.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <copyrite.h>
+#include <pbc.h>
+#include <rmpbc.h>
+#include <statutil.h>
+
+#include "gromacs/errorreporting/standarderrorreporter.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/asciihelpwriter.h"
+#include "gromacs/options/cmdlineparser.h"
+#include "gromacs/options/globalproperties.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/selectioncollection.h"
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+#include "gromacs/trajectoryanalysis/analysissettings.h"
+#include "gromacs/trajectoryanalysis/cmdlinerunner.h"
+#include "gromacs/trajectoryanalysis/runnercommon.h"
+
+namespace gmx
+{
+
+/********************************************************************
+ * TrajectoryAnalysisCommandLineRunner::Impl
+ */
+
+class TrajectoryAnalysisCommandLineRunner::Impl
+{
+ public:
+ Impl(TrajectoryAnalysisModule *module);
+ ~Impl();
+
+ void printHelp(const Options &options,
+ const TrajectoryAnalysisRunnerCommon &common);
+ int parseOptions(TrajectoryAnalysisSettings *settings,
+ TrajectoryAnalysisRunnerCommon *common,
+ SelectionCollection *selections,
+ Options *options,
+ int *argc, char *argv[]);
+
+ TrajectoryAnalysisModule *_module;
+ int _debugLevel;
+};
+
+
+TrajectoryAnalysisCommandLineRunner::Impl::Impl(
+ TrajectoryAnalysisModule *module)
+ : _module(module), _debugLevel(0)
+{
+}
+
+
+TrajectoryAnalysisCommandLineRunner::Impl::~Impl()
+{
+ delete _module;
+}
+
+
+void
+TrajectoryAnalysisCommandLineRunner::Impl::printHelp(
+ const Options &options,
+ const TrajectoryAnalysisRunnerCommon &common)
+{
+ TrajectoryAnalysisRunnerCommon::HelpFlags flags = common.helpFlags();
+ if (flags != 0)
+ {
+ AsciiHelpWriter(options)
+ .setShowDescriptions(flags & TrajectoryAnalysisRunnerCommon::efHelpShowDescriptions)
+ .setShowHidden(flags & TrajectoryAnalysisRunnerCommon::efHelpShowHidden)
+ .writeHelp(stderr);
+ }
+}
+
+
+int
+TrajectoryAnalysisCommandLineRunner::Impl::parseOptions(
+ TrajectoryAnalysisSettings *settings,
+ TrajectoryAnalysisRunnerCommon *common,
+ SelectionCollection *selections,
+ Options *options,
+ int *argc, char *argv[])
+{
+ StandardErrorReporter errors;
+ int rc;
+
+ Options *moduleOptions = _module->initOptions(settings);
+ if (moduleOptions == NULL)
+ {
+ GMX_ERROR(eeOutOfMemory,
+ "Could not allocate memory for option storage");
+ }
+
+ Options *commonOptions = common->initOptions();
+ if (moduleOptions == NULL)
+ {
+ GMX_ERROR(eeOutOfMemory,
+ "Could not allocate memory for option storage");
+ }
+
+ Options *selectionOptions = selections->initOptions();
+ if (selectionOptions == NULL)
+ {
+ GMX_ERROR(eeOutOfMemory,
+ "Could not allocate memory for option storage");
+ }
+
+ options->addSubSection(commonOptions);
+ options->addSubSection(selectionOptions);
+ options->addSubSection(moduleOptions);
+
+ options->globalProperties().setSelectionCollection(selections);
+ commonOptions->addDefaultOptions();
+
+ {
+ CommandLineParser parser(options, &errors);
+ rc = parser.parse(argc, argv);
+ printHelp(*options, *common);
+ if (rc != 0)
+ {
+ GMX_ERROR(rc, "Command-line option parsing failed, "
+ "see higher up for detailed error messages");
+ }
+ rc = options->finish(&errors);
+ if (rc != 0)
+ {
+ GMX_ERROR(rc, "Command-line option parsing failed, "
+ "see higher up for detailed error messages");
+ }
+ }
+
+ rc = common->initOptionsDone();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = _module->initOptionsDone(settings);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ rc = common->initIndexGroups(selections);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ // TODO: Check whether the input is a pipe.
+ bool bInteractive = true;
+ rc = selections->parseRequestedFromStdin(bInteractive, &errors);
+ common->doneIndexGroups(selections);
+ return rc;
+}
+
+
+/********************************************************************
+ * TrajectoryAnalysisCommandLineRunner
+ */
+
+TrajectoryAnalysisCommandLineRunner::TrajectoryAnalysisCommandLineRunner(
+ TrajectoryAnalysisModule *module)
+ : _impl(new Impl(module))
+{
+}
+
+
+TrajectoryAnalysisCommandLineRunner::~TrajectoryAnalysisCommandLineRunner()
+{
+ delete _impl;
+}
+
+
+void
+TrajectoryAnalysisCommandLineRunner::setSelectionDebugLevel(int debuglevel)
+{
+ _impl->_debugLevel = 1;
+}
+
+
+int
+TrajectoryAnalysisCommandLineRunner::run(int argc, char *argv[])
+{
+ TrajectoryAnalysisModule *module = _impl->_module;
+ int rc;
+
+ CopyRight(stderr, argv[0]);
+
+ SelectionCollection selections(NULL);
+ rc = selections.init();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ selections.setDebugLevel(_impl->_debugLevel);
+
+ TrajectoryAnalysisSettings settings;
+ TrajectoryAnalysisRunnerCommon common(&settings);
+
+ Options options(NULL, NULL);
+ rc = _impl->parseOptions(&settings, &common, &selections, &options,
+ &argc, argv);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ rc = common.initTopology(&selections);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = selections.compile();
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ const TopologyInformation &topology = common.topologyInformation();
+ rc = module->initAnalysis(topology);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ // Load first frame.
+ rc = common.initFirstFrame();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = module->initAfterFirstFrame(common.frame());
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ t_pbc pbc;
+ t_pbc *ppbc = settings.hasPBC() ? &pbc : 0;
+
+ int nframes = 0;
+ TrajectoryAnalysisModuleData *pdata = NULL;
+ rc = module->startFrames(NULL, selections, &pdata);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ do
+ {
+ rc = common.initFrame();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ t_trxframe &frame = common.frame();
+ if (ppbc)
+ {
+ set_pbc(ppbc, topology.ePBC(), frame.box);
+ }
+
+ rc = selections.evaluate(&frame, ppbc);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ rc = module->analyzeFrame(nframes, frame, ppbc, pdata);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ nframes++;
+ }
+ while (common.readNextFrame());
+ rc = module->finishFrames(pdata);
+ if (rc != 0)
+ {
+ return rc;
+ }
+ if (pdata)
+ {
+ rc = pdata->finish();
+ delete pdata;
+ if (rc != 0)
+ {
+ return rc;
+ }
+ }
+
+ if (common.hasTrajectory())
+ {
+ fprintf(stderr, "Analyzed %d frames, last time %.3f\n",
+ nframes, common.frame().time);
+ }
+ else
+ {
+ fprintf(stderr, "Analyzed topology coordinates\n");
+ }
+
+ // Restore the maximal groups for dynamic selections.
+ rc = selections.evaluateFinal(nframes);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ rc = module->finishAnalysis(nframes);
+ if (rc == 0)
+ {
+ rc = module->writeOutput();
+ }
+
+ return rc;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declares gmx::TrajectoryAnalysisCommandLineRunner.
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_CMDLINERUNNER_H
+#define GMX_TRAJECTORYANALYSIS_CMDLINERUNNER_H
+
+namespace gmx
+{
+
+class TrajectoryAnalysisModule;
+
+/*! \brief
+ * Implementation class for cmd-line analysis tools.
+ *
+ * This class implements a command-line analysis program, given a
+ * TrajectoryAnalysisModule object. It takes care of converting cmd-line
+ * parameters to a form understood by the module, as well as parsing common
+ * options, initializing and evaluating selections, and looping over trajectory
+ * frames.
+ */
+class TrajectoryAnalysisCommandLineRunner
+{
+ public:
+ TrajectoryAnalysisCommandLineRunner(TrajectoryAnalysisModule *module);
+ ~TrajectoryAnalysisCommandLineRunner();
+
+ void setSelectionDebugLevel(int debuglevel);
+ int run(int argc, char *argv[]);
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ TrajectoryAnalysisCommandLineRunner(const TrajectoryAnalysisCommandLineRunner &);
+ void operator =(const TrajectoryAnalysisCommandLineRunner &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::createTrajectoryAnalysisModule().
+ *
+ * \ingroup module_trajectoryanalysis
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include "gromacs/trajectoryanalysis/modules.h"
+
+#include <string2.h>
+
+#include "modules/distance.h"
+#include "modules/select.h"
+
+namespace
+{
+
+using namespace gmx::analysismodules;
+
+struct module_map_t
+{
+ const char *name;
+ gmx::TrajectoryAnalysisModule *(*creator)(void);
+};
+
+const module_map_t modules[] =
+{
+ {gmx::analysismodules::distance, Distance::create},
+ {gmx::analysismodules::select, Select::create},
+ {NULL, NULL},
+};
+
+} // namespace
+
+namespace gmx
+{
+
+TrajectoryAnalysisModule *createTrajectoryAnalysisModule(const char *name)
+{
+ size_t len = strlen(name);
+ int match_i = -1;
+
+ for (int i = 0; modules[i].name != NULL; ++i)
+ {
+ if (strncasecmp(name, modules[i].name, len) == 0)
+ {
+ if (strlen(modules[i].name) == len)
+ {
+ match_i = i;
+ break;
+ }
+ else if (match_i == -1)
+ {
+ match_i = i;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+ if (match_i != -1)
+ {
+ return modules[match_i].creator();
+ }
+ return NULL;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Generic interface for creation of trajectory analysis modules.
+ *
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_H
+
+namespace gmx
+{
+
+class TrajectoryAnalysisModule;
+
+/*! \brief
+ * Creates a TrajectoryAnalysisModule object corresponding to a name.
+ *
+ * \param[in] name Name of the module to create (recognized names are
+ * defined in modules.h).
+ * \returns An allocated TrajectoryAnalysisModule object, or NULL if \p name
+ * is not valid.
+ *
+ * In addition to recognizing exact matches on \p name, the function also
+ * identifies cases where \p name is a prefix of exactly one recognized name
+ * (exact matches always take precedence).
+ */
+TrajectoryAnalysisModule *createTrajectoryAnalysisModule(const char *name);
+
+namespace analysismodules
+{
+
+static const char * const distance = "distance";
+static const char * const select = "select";
+
+} // namespace modules
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+#include "distance.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pbc.h>
+#include <vec.h>
+
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/modules/average.h"
+#include "gromacs/analysisdata/modules/plot.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectionoption.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+Distance::Distance()
+ : _options("distance", "Distance calculation")
+{
+ _sel[0] = _sel[1] = NULL;
+}
+
+
+Distance::~Distance()
+{
+}
+
+
+Options *
+Distance::initOptions(TrajectoryAnalysisSettings *settings)
+{
+ static const char *const desc[] = {
+ "g_dist can calculate the distance between two positions as",
+ "a function of time. The total distance and its",
+ "x, y and z components are plotted.",
+ NULL
+ };
+
+ _options.setDescription(desc);
+
+ _options.addOption(FileNameOption("o").filetype(eftPlot).writeOnly()
+ .store(&_fnDist).defaultValue("dist"));
+ _options.addOption(SelectionOption("select").required().valueCount(2)
+ .store(_sel));
+ return &_options;
+}
+
+
+int
+Distance::initAnalysis(const TopologyInformation & /*top*/)
+{
+ if (_sel[0]->posCount() != 1)
+ {
+ GMX_ERROR(eeInvalidInput,
+ "The first selection does not define a single position");
+ }
+ if (_sel[1]->posCount() != 1)
+ {
+ GMX_ERROR(eeInvalidInput,
+ "The second selection does not define a single position");
+ }
+ _data.setColumns(4);
+ registerAnalysisDataset(&_data, "distance");
+
+ _avem = new AnalysisDataAverageModule();
+ _data.addModule(_avem);
+
+ _plotm = new AnalysisDataPlotModule(_options);
+ _plotm->setFileName(_fnDist);
+ _plotm->setTitle("Distance");
+ _plotm->setXLabel("Time [ps]");
+ _plotm->setYLabel("Distance [nm]");
+ _data.addModule(_plotm);
+
+ return 0;
+}
+
+
+int
+Distance::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+ TrajectoryAnalysisModuleData *pdata)
+{
+ AnalysisDataHandle *dh = pdata->dataHandle("distance");
+ Selection *sel1 = pdata->parallelSelection(_sel[0]);
+ Selection *sel2 = pdata->parallelSelection(_sel[1]);
+ rvec dx;
+ real r;
+
+ if (pbc != NULL)
+ {
+ pbc_dx(pbc, sel1->x(0), sel2->x(0), dx);
+ }
+ else
+ {
+ rvec_sub(sel1->x(0), sel2->x(0), dx);
+ }
+ r = norm(dx);
+ dh->startFrame(frnr, fr.time);
+ dh->addPoint(0, r);
+ dh->addPoints(1, 3, dx);
+ dh->finishFrame();
+ return 0;
+}
+
+
+int
+Distance::finishAnalysis(int /*nframes*/)
+{
+ return 0;
+}
+
+
+int
+Distance::writeOutput()
+{
+ const real *ave;
+
+ _avem->getData(0, NULL, &ave, NULL);
+ fprintf(stderr, "Average distance: %f\n", ave[0]);
+ fprintf(stderr, "Std. deviation: %f\n", ave[1]);
+ return 0;
+}
+
+
+TrajectoryAnalysisModule *
+Distance::create()
+{
+ return new Distance();
+}
+
+} // namespace analysismodules
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+#ifndef GMX_TRAJANA_MODULES_DISTANCE_HPP
+#define GMX_TRAJANA_MODULES_DISTANCE_HPP
+
+#include <string>
+#include <vector>
+
+#include "../analysismodule.h"
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/options/options.h"
+
+namespace gmx
+{
+
+class AnalysisDataAverageModule;
+class AnalysisDataPlotModule;
+class Selection;
+
+namespace analysismodules
+{
+
+class Distance : public TrajectoryAnalysisModule
+{
+ public:
+ Distance();
+ virtual ~Distance();
+
+ static TrajectoryAnalysisModule *create();
+
+ virtual Options *initOptions(TrajectoryAnalysisSettings *settings);
+ virtual int initAnalysis(const TopologyInformation &top);
+
+ virtual int analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+ TrajectoryAnalysisModuleData *pdata);
+
+ virtual int finishAnalysis(int nframes);
+ virtual int writeOutput();
+
+ private:
+ Options _options;
+ std::string _fnDist;
+ Selection *_sel[2];
+ AnalysisData _data;
+ AnalysisDataAverageModule *_avem;
+ AnalysisDataPlotModule *_plotm;
+
+ // Copy and assign disallowed by base.
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+#include "select.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <index.h>
+#include <smalloc.h>
+#include <string2.h>
+
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/modules/plot.h"
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectionoption.h"
+#include "gromacs/trajectoryanalysis/analysissettings.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class Select::ModuleData : public TrajectoryAnalysisModuleData
+{
+ public:
+ ModuleData() : _mmap(NULL)
+ {
+ }
+
+ virtual ~ModuleData()
+ {
+ if (_mmap)
+ {
+ gmx_ana_indexmap_deinit(_mmap);
+ sfree(_mmap);
+ }
+ }
+
+ virtual int finish()
+ {
+ return finishDataHandles();
+ }
+
+ gmx_ana_indexmap_t *_mmap;
+};
+
+
+Select::Select()
+ : _options("select", "Selection information"),
+ _bTotNorm(false), _bFracNorm(false), _bResInd(false),
+ _block(NULL), _gnames(NULL)
+{
+}
+
+
+Select::~Select()
+{
+ if (_block != NULL)
+ {
+ done_blocka(_block);
+ sfree(_block);
+ }
+}
+
+
+Options *
+Select::initOptions(TrajectoryAnalysisSettings *settings)
+{
+ static const char *const desc[] = {
+ "g_select",
+ NULL
+ };
+
+ _options.setDescription(desc);
+
+ _options.addOption(FileNameOption("os").filetype(eftPlot).writeOnly()
+ .store(&_fnSize).defaultValue("size"));
+ _options.addOption(FileNameOption("oc").filetype(eftPlot).writeOnly()
+ .store(&_fnFrac).defaultValue("frac"));
+ _options.addOption(FileNameOption("oi").filetype(eftPlot).writeOnly()
+ .store(&_fnIndex).defaultValue("index"));
+ _options.addOption(FileNameOption("on").filetype(eftIndex).writeOnly()
+ .store(&_fnNdx).defaultValue("index"));
+ _options.addOption(FileNameOption("om").filetype(eftPlot).writeOnly()
+ .store(&_fnMask).defaultValue("mask"));
+
+ _options.addOption(SelectionOption("select").required().multiValue()
+ .storeVector(&_sel));
+
+ _options.addOption(BooleanOption("norm").store(&_bTotNorm)
+ .description("Normalize by total number of positions with -os"));
+ _options.addOption(BooleanOption("cfnorm").store(&_bFracNorm)
+ .description("Normalize by covered fraction with -os"));
+ _options.addOption(BooleanOption("resind").store(&_bResInd)
+ .description("Write unique residue index instead of residue number"));
+
+ return &_options;
+}
+
+
+int
+Select::initAnalysis(const TopologyInformation &top)
+{
+ for (std::vector<Selection *>::const_iterator i = _sel.begin(); i != _sel.end(); ++i)
+ {
+ (*i)->initCoveredFraction(CFRAC_SOLIDANGLE);
+ }
+
+ _sdata.setColumns(_sel.size());
+ registerAnalysisDataset(&_sdata, "size");
+ snew(_totsize, _sel.size());
+ for (size_t g = 0; g < _sel.size(); ++g)
+ {
+ _totsize[g] = _bTotNorm ? _sel[g]->posCount() : 1;
+ }
+ if (!_fnSize.empty())
+ {
+ AnalysisDataPlotModule *plot = new AnalysisDataPlotModule(_options);
+ plot->setFileName(_fnSize);
+ plot->setTitle("Selection size");
+ plot->setXLabel("Time [ps]");
+ plot->setYLabel("Number");
+ _sdata.addModule(plot);
+ }
+
+ _cdata.setColumns(_sel.size());
+ registerAnalysisDataset(&_cdata, "cfrac");
+ if (!_fnFrac.empty())
+ {
+ AnalysisDataPlotModule *plot = new AnalysisDataPlotModule(_options);
+ plot->setFileName(_fnFrac);
+ plot->setTitle("Covered fraction");
+ plot->setXLabel("Time [ps]");
+ plot->setYLabel("Fraction");
+ plot->setYFormat(6, 4);
+ _cdata.addModule(plot);
+ }
+
+ _idata.setColumns(2, true);
+ registerAnalysisDataset(&_idata, "index");
+ if (!_fnIndex.empty())
+ {
+ AnalysisDataPlotModule *plot = new AnalysisDataPlotModule(_options);
+ plot->setFileName(_fnIndex);
+ plot->setPlainOutput(true);
+ plot->setYFormat(4, 0);
+ _idata.addModule(plot);
+ }
+
+ if (!_fnNdx.empty())
+ {
+ _block = new_blocka();
+ _gnames = NULL;
+ _modnames.reserve(_sel.size());
+ for (size_t g = 0; g < _sel.size(); ++g)
+ {
+ _modnames.push_back(_sel[g]->name());
+ size_t pos;
+ while ((pos = _modnames[g].find(' ')) != std::string::npos)
+ {
+ _modnames[g][pos] = '_';
+ }
+
+ if (!_sel[g]->isDynamic())
+ {
+ add_grp(_block, &_gnames, _sel[g]->posCount(),
+ _sel[g]->mapIds(), _modnames[g].c_str());
+ _modnames[g].clear();
+ }
+ }
+ }
+
+ _mdata.setColumns(_sel[0]->posCount());
+ registerAnalysisDataset(&_mdata, "mask");
+ if (!_fnMask.empty())
+ {
+ if (_sel.size() > 1U)
+ {
+ fprintf(stderr, "WARNING: the mask (-om) will only be written for the first group\n");
+ }
+ if (!_sel[0]->isDynamic())
+ {
+ fprintf(stderr, "WARNING: will not write the mask (-om) for a static selection\n");
+ }
+ else
+ {
+ AnalysisDataPlotModule *plot = new AnalysisDataPlotModule(_options);
+ plot->setFileName(_fnMask);
+ plot->setTitle("Selection mask");
+ plot->setXLabel("Time [ps]");
+ plot->setYLabel("Occupancy");
+ plot->setYFormat(1, 0);
+ _mdata.addModule(plot);
+ }
+ }
+
+ _top = top.topology();
+
+ return 0;
+}
+
+
+int
+Select::startFrames(AnalysisDataParallelOptions opt,
+ const SelectionCollection &selections,
+ TrajectoryAnalysisModuleData **pdatap)
+{
+ ModuleData *pdata = new ModuleData();
+
+ *pdatap = pdata;
+ int rc = pdata->init(this, opt, selections);
+ if (rc != 0)
+ {
+ delete pdata;
+ *pdatap = NULL;
+ return rc;
+ }
+ snew(pdata->_mmap, 1);
+ gmx_ana_indexmap_init(pdata->_mmap, pdata->parallelSelection(_sel[0])->indexGroup(),
+ _top, _sel[0]->type());
+ return 0;
+}
+
+
+int
+Select::analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+ TrajectoryAnalysisModuleData *pdata)
+{
+ ModuleData *d = static_cast<ModuleData *>(pdata);
+ AnalysisDataHandle *sdh = pdata->dataHandle("size");
+ AnalysisDataHandle *cdh = pdata->dataHandle("cfrac");
+ AnalysisDataHandle *idh = pdata->dataHandle("index");
+ AnalysisDataHandle *mdh = pdata->dataHandle("mask");
+ std::vector<Selection *> sel(pdata->parallelSelections(_sel));
+
+ if (sdh != NULL)
+ {
+ sdh->startFrame(frnr, fr.time);
+ for (size_t g = 0; g < sel.size(); ++g)
+ {
+ real normfac = _bFracNorm ? 1.0 / sel[g]->cfrac() : 1.0;
+ normfac /= _totsize[g];
+ sdh->addPoint(g, sel[g]->posCount() * normfac);
+ }
+ sdh->finishFrame();
+ }
+
+ if (cdh != NULL)
+ {
+ cdh->startFrame(frnr, fr.time);
+ for (size_t g = 0; g < sel.size(); ++g)
+ {
+ cdh->addPoint(g, sel[g]->cfrac());
+ }
+ cdh->finishFrame();
+ }
+
+ if (idh != NULL)
+ {
+ idh->startFrame(frnr, fr.time);
+ for (size_t g = 0; g < sel.size(); ++g)
+ {
+ idh->addPoint(0, sel[g]->posCount());
+ for (int i = 0; i < sel[g]->posCount(); ++i)
+ {
+ if (sel[g]->type() == INDEX_RES && !_bResInd)
+ {
+ idh->addPoint(1, _top->atoms.resinfo[sel[g]->mapId(i)].nr);
+ }
+ else
+ {
+ idh->addPoint(1, sel[g]->mapId(i) + 1);
+ }
+ }
+ }
+ idh->finishFrame();
+ }
+
+ /** TODO: This is not thread-safe */
+ if (_block != NULL)
+ {
+ for (size_t g = 0; g < sel.size(); ++g)
+ {
+ if (sel[g]->isDynamic())
+ {
+ char tbuf[50];
+ char *buf;
+
+ sprintf(tbuf, "_%.3f", fr.time);
+ snew(buf, _modnames[g].size() + strlen(tbuf) + 1);
+ strcpy(buf, _modnames[g].c_str());
+ strcat(buf, tbuf);
+ add_grp(_block, &_gnames, sel[g]->posCount(),
+ sel[g]->mapIds(), buf);
+ sfree(buf);
+ }
+ }
+ }
+
+ if (mdh != NULL)
+ {
+ gmx_ana_indexmap_update(d->_mmap, sel[0]->indexGroup(), TRUE);
+ mdh->startFrame(frnr, fr.time);
+ for (int b = 0; b < d->_mmap->nr; ++b)
+ {
+ mdh->addPoint(b, d->_mmap->refid[b] == -1 ? 0 : 1);
+ }
+ mdh->finishFrame();
+ }
+ return 0;
+}
+
+
+int
+Select::finishAnalysis(int /*nframes*/)
+{
+ return 0;
+}
+
+
+int
+Select::writeOutput()
+{
+ if (!_fnNdx.empty())
+ {
+ write_index(_fnNdx.c_str(), _block, _gnames);
+ }
+
+ return 0;
+}
+
+
+TrajectoryAnalysisModule *
+Select::create()
+{
+ return new Select();
+}
+
+} // namespace analysismodules
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_SELECT_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_SELECT_H
+
+#include <string>
+#include <vector>
+
+#include "../analysismodule.h"
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/options/options.h"
+
+namespace gmx
+{
+
+class AnalysisDataPlotModule;
+class Selection;
+
+namespace analysismodules
+{
+
+class Select : public TrajectoryAnalysisModule
+{
+ public:
+ Select();
+ virtual ~Select();
+
+ static TrajectoryAnalysisModule *create();
+
+ virtual Options *initOptions(TrajectoryAnalysisSettings *settings);
+ virtual int initAnalysis(const TopologyInformation &top);
+
+ virtual int startFrames(AnalysisDataParallelOptions opt,
+ const SelectionCollection &selections,
+ TrajectoryAnalysisModuleData **pdatap);
+ virtual int analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+ TrajectoryAnalysisModuleData *pdata);
+
+ virtual int finishAnalysis(int nframes);
+ virtual int writeOutput();
+
+ private:
+ class ModuleData;
+
+ Options _options;
+ std::vector<Selection *> _sel;
+
+ std::string _fnSize;
+ std::string _fnFrac;
+ std::string _fnIndex;
+ std::string _fnNdx;
+ std::string _fnMask;
+ bool _bTotNorm;
+ bool _bFracNorm;
+ bool _bResInd;
+
+ t_topology *_top;
+ int *_totsize;
+ AnalysisData _sdata;
+ AnalysisData _cdata;
+ AnalysisData _idata;
+ AnalysisData _mdata;
+ std::vector<std::string> _modnames;
+ t_blocka *_block;
+ char **_gnames;
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::TrajectoryAnalysisRunnerCommon.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <cassert>
+#include <string.h>
+
+#include <rmpbc.h>
+#include <smalloc.h>
+#include <statutil.h>
+#include <tpxio.h>
+#include <vec.h>
+
+#include "gromacs/fatalerror/fatalerror.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/globalproperties.h"
+#include "gromacs/options/options.h"
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/selectioncollection.h"
+#include "gromacs/trajectoryanalysis/analysissettings.h"
+#include "gromacs/trajectoryanalysis/runnercommon.h"
+
+#include "analysissettings-impl.h"
+
+namespace gmx
+{
+
+class TrajectoryAnalysisRunnerCommon::Impl
+{
+ public:
+ Impl(TrajectoryAnalysisSettings *settings);
+ ~Impl();
+
+ void finishTrajectory();
+
+ TrajectoryAnalysisSettings &_settings;
+ Options _options;
+ TopologyInformation _topInfo;
+
+ bool _bHelp;
+ bool _bShowHidden;
+ bool _bQuiet;
+ //! Name of the trajectory file (empty if not provided).
+ std::string _trjfile;
+ //! Name of the topology file (empty if no topology provided).
+ std::string _topfile;
+ //! Name of the index file (empty if no index file provided).
+ std::string _ndxfile;
+ double _startTime;
+ double _endTime;
+ double _deltaTime;
+
+ gmx_ana_indexgrps_t *_grps;
+ bool _bTrajOpen;
+ //! The current frame, or \p NULL if no frame loaded yet.
+ t_trxframe *fr;
+ gmx_rmpbc_t _gpbc;
+ //! Used to store the status variable from read_first_frame().
+ t_trxstatus *_status;
+ output_env_t _oenv;
+};
+
+
+TrajectoryAnalysisRunnerCommon::Impl::Impl(TrajectoryAnalysisSettings *settings)
+ : _settings(*settings), _options("common", "Common analysis control"),
+ _bHelp(false), _bShowHidden(false), _bQuiet(false),
+ _startTime(0.0), _endTime(0.0), _deltaTime(0.0),
+ _grps(NULL),
+ _bTrajOpen(false), fr(NULL), _gpbc(NULL), _status(NULL), _oenv(NULL)
+{
+}
+
+
+TrajectoryAnalysisRunnerCommon::Impl::~Impl()
+{
+ if (_grps != NULL)
+ {
+ gmx_ana_indexgrps_free(_grps);
+ }
+ finishTrajectory();
+ if (fr)
+ {
+ // There doesn't seem to be a function for freeing frame data
+ sfree(fr->x);
+ sfree(fr->v);
+ sfree(fr->f);
+ sfree(fr);
+ }
+}
+
+
+void
+TrajectoryAnalysisRunnerCommon::Impl::finishTrajectory()
+{
+ if (_bTrajOpen)
+ {
+ close_trx(_status);
+ _bTrajOpen = false;
+ }
+ if (_gpbc != NULL)
+ {
+ gmx_rmpbc_done(_gpbc);
+ _gpbc = NULL;
+ }
+}
+
+/*********************************************************************
+ * TrajectoryAnalysisRunnerCommon
+ */
+
+TrajectoryAnalysisRunnerCommon::TrajectoryAnalysisRunnerCommon(
+ TrajectoryAnalysisSettings *settings)
+ : _impl(new Impl(settings))
+{
+}
+
+
+TrajectoryAnalysisRunnerCommon::~TrajectoryAnalysisRunnerCommon()
+{
+ delete _impl;
+}
+
+
+Options *
+TrajectoryAnalysisRunnerCommon::initOptions()
+{
+ TrajectoryAnalysisSettings &settings = _impl->_settings;
+ Options &options = _impl->_options;
+
+ // Add options for help.
+ options.addOption(BooleanOption("h").store(&_impl->_bHelp)
+ .description("Print help and quit"));
+ options.addOption(BooleanOption("hidden").store(&_impl->_bShowHidden)
+ .hidden()
+ .description("Show hidden options"));
+ options.addOption(BooleanOption("quiet").store(&_impl->_bQuiet)
+ .hidden()
+ .description("Hide options in normal run"));
+
+ // Add common file name arguments.
+ options.addOption(FileNameOption("f")
+ .filetype(eftTrajectory).readOnly()
+ .store(&_impl->_trjfile)
+ .description("Input trajectory"));
+ options.addOption(FileNameOption("s")
+ .filetype(eftTopology).readOnly()
+ .store(&_impl->_topfile)
+ .description("Input topology"));
+ options.addOption(FileNameOption("n")
+ .filetype(eftIndex).readOnly()
+ .store(&_impl->_ndxfile)
+ .description("Extra index groups"));
+
+ // Add options for trajectory time control.
+ options.addOption(DoubleOption("b").store(&_impl->_startTime).timeValue()
+ .description("First frame (%t) to read from trajectory"));
+ options.addOption(DoubleOption("e").store(&_impl->_endTime).timeValue()
+ .description("Last frame (%t) to read from trajectory"));
+ options.addOption(DoubleOption("dt").store(&_impl->_deltaTime).timeValue()
+ .description("Only use frame if t MOD dt == first time (%t)"));
+
+ // Add common options for trajectory processing.
+ if (!settings.hasFlag(TrajectoryAnalysisSettings::efNoUserRmPBC))
+ {
+ options.addOption(BooleanOption("rmpbc").store(&settings._impl->bRmPBC)
+ .description("Make molecules whole for each frame"));
+ }
+ if (!settings.hasFlag(TrajectoryAnalysisSettings::efNoUserPBC))
+ {
+ options.addOption(BooleanOption("pbc").store(&settings._impl->bPBC)
+ .description("Use periodic boundary conditions for distance calculation"));
+ }
+
+ return &_impl->_options;
+}
+
+
+int
+TrajectoryAnalysisRunnerCommon::initOptionsDone()
+{
+ if (_impl->_bHelp)
+ {
+ // TODO: Proper error code for graceful exit
+ return -1;
+ }
+
+ if (_impl->_trjfile.empty() && _impl->_topfile.empty())
+ {
+ GMX_ERROR(eeInconsistentInput,
+ "No trajectory or topology provided, nothing to do!");
+ }
+
+ if (_impl->_options.isSet("b"))
+ setTimeValue(TBEGIN, _impl->_startTime);
+ if (_impl->_options.isSet("e"))
+ setTimeValue(TEND, _impl->_endTime);
+ if (_impl->_options.isSet("dt"))
+ setTimeValue(TDELTA, _impl->_deltaTime);
+
+ return 0;
+}
+
+
+int
+TrajectoryAnalysisRunnerCommon::initIndexGroups(SelectionCollection *selections)
+{
+ int rc = 0;
+
+ if (_impl->_ndxfile.empty())
+ {
+ // TODO: Initialize default selections
+ selections->setIndexGroups(NULL);
+ }
+ else
+ {
+ gmx_ana_indexgrps_init(&_impl->_grps, NULL, _impl->_ndxfile.c_str());
+ rc = selections->setIndexGroups(_impl->_grps);
+ }
+ return rc;
+}
+
+
+void
+TrajectoryAnalysisRunnerCommon::doneIndexGroups(SelectionCollection *selections)
+{
+ if (_impl->_grps != NULL)
+ {
+ selections->setIndexGroups(NULL);
+ gmx_ana_indexgrps_free(_impl->_grps);
+ _impl->_grps = NULL;
+ }
+}
+
+
+int
+TrajectoryAnalysisRunnerCommon::initTopology(SelectionCollection *selections)
+{
+ const TrajectoryAnalysisSettings &settings = _impl->_settings;
+ bool bRequireTop
+ = settings.hasFlag(TrajectoryAnalysisSettings::efRequireTop)
+ || selections->requiresTopology();
+ if (bRequireTop && _impl->_topfile.empty())
+ {
+ GMX_ERROR(eeInconsistentInput,
+ "No topology provided, but one is required for analysis");
+ }
+
+ // Load the topology if requested.
+ if (!_impl->_topfile.empty())
+ {
+ char title[STRLEN];
+
+ snew(_impl->_topInfo._top, 1);
+ _impl->_topInfo._bTop = read_tps_conf(_impl->_topfile.c_str(), title,
+ _impl->_topInfo._top, &_impl->_topInfo._ePBC,
+ &_impl->_topInfo._xtop, NULL, _impl->_topInfo._boxtop, TRUE);
+ if (hasTrajectory()
+ && !settings.hasFlag(TrajectoryAnalysisSettings::efUseTopX))
+ {
+ sfree(_impl->_topInfo._xtop);
+ _impl->_topInfo._xtop = NULL;
+ }
+ }
+
+ // Read the first frame if we don't know the maximum number of atoms
+ // otherwise.
+ int natoms = -1;
+ if (!_impl->_topInfo.hasTopology())
+ {
+ int rc = initFirstFrame();
+ if (rc != 0)
+ {
+ return rc;
+ }
+ natoms = _impl->fr->natoms;
+ }
+ int rc = selections->setTopology(_impl->_topInfo.topology(), natoms);
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ /*
+ if (_impl->bSelDump)
+ {
+ gmx_ana_poscalc_coll_print_tree(stderr, _impl->pcc);
+ fprintf(stderr, "\n");
+ }
+ */
+
+ return 0;
+}
+
+
+int
+TrajectoryAnalysisRunnerCommon::initFirstFrame()
+{
+ // Return if we have already initialized the trajectory.
+ if (_impl->fr)
+ {
+ return 0;
+ }
+ _impl->_oenv = _impl->_options.globalProperties().output_env();
+
+ int frflags = _impl->_settings.frflags();
+ frflags |= TRX_NEED_X;
+
+ snew(_impl->fr, 1);
+
+ const TopologyInformation &top = _impl->_topInfo;
+ if (hasTrajectory())
+ {
+ if (!read_first_frame(_impl->_oenv, &_impl->_status,
+ _impl->_trjfile.c_str(), _impl->fr, frflags))
+ {
+ GMX_ERROR(eeFileNotFound,
+ "Could not read coordinates from trajectory");
+ }
+ _impl->_bTrajOpen = true;
+
+ if (top.hasTopology() && _impl->fr->natoms > top.topology()->atoms.nr)
+ {
+ fatalErrorFormatted(eeInconsistentInput, GMX_ERRORLOC,
+ "Trajectory (%d atoms) does not match topology (%d atoms)",
+ _impl->fr->natoms, top.topology()->atoms.nr);
+ return eeInconsistentInput;
+ }
+ // Check index groups if they have been initialized based on the topology.
+ /*
+ if (top)
+ {
+ for (int i = 0; i < _impl->sel->nr(); ++i)
+ {
+ gmx_ana_index_check(_impl->sel->sel(i)->indexGroup(),
+ _impl->fr->natoms);
+ }
+ }
+ */
+ }
+ else
+ {
+ // Prepare a frame from topology information.
+ // TODO: Initialize more of the fields.
+ if (frflags & (TRX_NEED_V))
+ {
+ GMX_ERROR(eeNotImplemented,
+ "Velocity reading from a topology not implemented");
+ }
+ if (frflags & (TRX_NEED_F))
+ {
+ GMX_ERROR(eeInvalidInput,
+ "Forces cannot be read from a topology");
+ }
+ _impl->fr->flags = frflags;
+ _impl->fr->natoms = top.topology()->atoms.nr;
+ _impl->fr->bX = TRUE;
+ snew(_impl->fr->x, _impl->fr->natoms);
+ memcpy(_impl->fr->x, top._xtop,
+ sizeof(*_impl->fr->x) * _impl->fr->natoms);
+ _impl->fr->bBox = TRUE;
+ copy_mat(const_cast<rvec *>(top._boxtop), _impl->fr->box);
+ }
+
+ set_trxframe_ePBC(_impl->fr, top.ePBC());
+ if (top.hasTopology() && _impl->_settings.hasRmPBC())
+ {
+ _impl->_gpbc = gmx_rmpbc_init(&top.topology()->idef, top.ePBC(),
+ _impl->fr->natoms, _impl->fr->box);
+ }
+
+ return 0;
+}
+
+
+bool
+TrajectoryAnalysisRunnerCommon::readNextFrame()
+{
+ bool bContinue = FALSE;
+ if (hasTrajectory())
+ {
+ bContinue = read_next_frame(_impl->_oenv, _impl->_status, _impl->fr);
+ }
+ if (!bContinue)
+ {
+ _impl->finishTrajectory();
+ }
+ return bContinue;
+}
+
+
+int
+TrajectoryAnalysisRunnerCommon::initFrame()
+{
+ if (_impl->_gpbc != NULL)
+ {
+ gmx_rmpbc_trxfr(_impl->_gpbc, _impl->fr);
+ }
+ return 0;
+}
+
+
+TrajectoryAnalysisRunnerCommon::HelpFlags
+TrajectoryAnalysisRunnerCommon::helpFlags() const
+{
+ HelpFlags flags = 0;
+
+ if (!_impl->_bQuiet)
+ {
+ flags |= efHelpShowOptions;
+ if (_impl->_bHelp)
+ {
+ flags |= efHelpShowDescriptions;
+ }
+ if (_impl->_bShowHidden)
+ {
+ flags |= efHelpShowHidden;
+ }
+ }
+ return flags;
+}
+
+bool
+TrajectoryAnalysisRunnerCommon::hasTrajectory() const
+{
+ return !_impl->_trjfile.empty();
+}
+
+
+const TopologyInformation &
+TrajectoryAnalysisRunnerCommon::topologyInformation() const
+{
+ return _impl->_topInfo;
+}
+
+
+t_trxframe &
+TrajectoryAnalysisRunnerCommon::frame() const
+{
+ assert(_impl->fr != NULL);
+ return *_impl->fr;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \file
+ * \brief
+ * Declaration of TrajanaModule and integrally related classes.
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_RUNNERCOMMON_H
+#define GMX_TRAJECTORYANALYSIS_RUNNERCOMMON_H
+
+#include <typedefs.h>
+
+namespace gmx
+{
+
+class Options;
+class SelectionCollection;
+class TopologyInformation;
+class TrajectoryAnalysisSettings;
+
+class TrajectoryAnalysisRunnerCommon
+{
+ public:
+ enum HelpFlag
+ {
+ efHelpShowOptions = 1<<0,
+ efHelpShowHidden = 1<<1,
+ efHelpShowDescriptions = 1<<2,
+ };
+ //! Combination of ::HelpFlag values.
+ typedef unsigned long HelpFlags;
+
+ explicit TrajectoryAnalysisRunnerCommon(TrajectoryAnalysisSettings *settings);
+ ~TrajectoryAnalysisRunnerCommon();
+
+ Options *initOptions();
+ int initOptionsDone();
+ int initIndexGroups(SelectionCollection *selections);
+ void doneIndexGroups(SelectionCollection *selections);
+ int initTopology(SelectionCollection *selections);
+ int initFirstFrame();
+ bool readNextFrame();
+ int initFrame();
+
+ //! Returns flags for help printing.
+ HelpFlags helpFlags() const;
+ //! Returns true if input data comes from a trajectory.
+ bool hasTrajectory() const;
+ const TopologyInformation &topologyInformation() const;
+ //! Returns the currently loaded frame.
+ t_trxframe &frame() const;
+
+ private:
+ class Impl;
+
+ Impl *_impl;
+
+ // Disallow copy and assign.
+ TrajectoryAnalysisRunnerCommon(const TrajectoryAnalysisRunnerCommon &);
+ void operator =(const TrajectoryAnalysisRunnerCommon &);
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+add_executable(test_selection test_selection.cpp)
+target_link_libraries(test_selection libgromacs)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief Testing/debugging tool for the selection engine.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <vector>
+
+#include <gromacs/options/basicoptions.h>
+#include <gromacs/options/options.h>
+#include <gromacs/selection/selection.h>
+#include <gromacs/selection/selectionoption.h>
+#include <gromacs/trajectoryanalysis/analysismodule.h>
+#include <gromacs/trajectoryanalysis/analysissettings.h>
+#include <gromacs/trajectoryanalysis/cmdlinerunner.h>
+
+namespace gmx
+{
+
+class SelectionTester : public TrajectoryAnalysisModule
+{
+ public:
+ SelectionTester();
+ ~SelectionTester();
+
+ Options *initOptions(TrajectoryAnalysisSettings *settings);
+ int initAnalysis(const TopologyInformation &top);
+
+ int analyzeFrame(int frnr, const t_trxframe &fr, t_pbc *pbc,
+ TrajectoryAnalysisModuleData *pdata);
+
+ int finishAnalysis(int nframes);
+ int writeOutput();
+
+ private:
+ void printSelections();
+
+ Options _options;
+ std::vector<Selection *> _selections;
+ int _nmaxind;
+};
+
+SelectionTester::SelectionTester()
+ : _options("testing", "Selection testing and debugging"),
+ _nmaxind(20)
+{
+}
+
+SelectionTester::~SelectionTester()
+{
+}
+
+void
+SelectionTester::printSelections()
+{
+ fprintf(stderr, "\nSelections:\n");
+ for (size_t g = 0; g < _selections.size(); ++g)
+ {
+ _selections[g]->printDebugInfo(_nmaxind);
+ }
+ fprintf(stderr, "\n");
+}
+
+Options *
+SelectionTester::initOptions(TrajectoryAnalysisSettings * /*settings*/)
+{
+ static const char *const desc[] = {
+ "This is a test program for selections.",
+ NULL
+ };
+
+ _options.setDescription(desc);
+
+ _options.addOption(SelectionOption("select").storeVector(&_selections)
+ .required().multiValue().allowMultiple()
+ .description("Selections to test"));
+ _options.addOption(IntegerOption("pmax").store(&_nmaxind)
+ .description("Maximum number of indices to print in lists (-1 = print all)"));
+
+ return &_options;
+}
+
+int
+SelectionTester::initAnalysis(const TopologyInformation &/*top*/)
+{
+ printSelections();
+ return 0;
+}
+
+int
+SelectionTester::analyzeFrame(int /*frnr*/, const t_trxframe &/*fr*/, t_pbc * /*pbc*/,
+ TrajectoryAnalysisModuleData * /*pdata*/)
+{
+ int g, i, n;
+
+ fprintf(stderr, "\n");
+ for (size_t g = 0; g < _selections.size(); ++g)
+ {
+ const Selection *sel = _selections[g];
+
+ gmx_ana_index_dump(sel->indexGroup(), g, _nmaxind);
+ fprintf(stderr, " Positions (%d pcs):\n", sel->posCount());
+ n = sel->posCount();
+ if (_nmaxind >= 0 && n > _nmaxind)
+ {
+ n = _nmaxind;
+ }
+ for (i = 0; i < n; ++i)
+ {
+ fprintf(stderr, " (%.2f,%.2f,%.2f) r=%d, m=%d, n=%d\n",
+ sel->x(i)[XX], sel->x(i)[YY], sel->x(i)[ZZ],
+ sel->refId(i), sel->mapId(i),
+ sel->atomCount(i));
+ }
+ if (n < sel->posCount())
+ {
+ fprintf(stderr, " ...\n");
+ }
+ }
+ fprintf(stderr, "\n");
+ return 0;
+}
+
+int
+SelectionTester::finishAnalysis(int /*nframes*/)
+{
+ printSelections();
+ return 0;
+}
+
+int
+SelectionTester::writeOutput()
+{
+ return 0;
+}
+
+}
+
+int
+main(int argc, char *argv[])
+{
+ gmx::TrajectoryAnalysisCommandLineRunner runner(new gmx::SelectionTester());
+ runner.setSelectionDebugLevel(1);
+ return runner.run(argc, argv);
+}
--- /dev/null
+set(UTILITY_PUBLIC_HEADERS
+ flags.h)
+install(FILES ${UTILITY_PUBLIC_HEADERS}
+ DESTINATION ${INCL_INSTALL_DIR}/gromacs/utility
+ COMPONENT development)
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::FlagsTemplate.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ */
+#ifndef GMX_UTILITY_FLAGS_H
+#define GMX_UTILITY_FLAGS_H
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Template class for typesafe handling of combination of flags.
+ *
+ * \tparam T An enumerated type that holds the possible single flags.
+ *
+ * \inlibraryapi
+ */
+template <typename T>
+class FlagsTemplate
+{
+ public:
+ //! Creates a flags object with no flags set.
+ FlagsTemplate() : _flags(0) {}
+ //! Creates a flags object from a single flag.
+ FlagsTemplate(T flag) : _flags(flag) {}
+
+ //! Returns true if the given flag is set.
+ bool test(T flag) const { return _flags & flag; }
+ //! Clears all flags.
+ void clearAll() { _flags = 0; }
+ //! Sets the given flag.
+ void set(T flag) { _flags |= flag; }
+ //! Clears the given flag.
+ void clear(T flag) { _flags &= ~flag; }
+ //! Sets or clears the given flag.
+ void set(T flag, bool bSet)
+ {
+ if (bSet)
+ {
+ set(flag);
+ }
+ else
+ {
+ clear(flag);
+ }
+ }
+
+ //! Combines flags from two flags objects.
+ FlagsTemplate<T> operator |(const FlagsTemplate<T> &other) const
+ {
+ return FlagsTemplate<T>(_flags | other._flags);
+ }
+ //! Combines flags from another flag object.
+ FlagsTemplate<T> &operator |=(const FlagsTemplate<T> &other)
+ {
+ _flags |= other._flags;
+ return *this;
+ }
+
+ private:
+ //! Creates a flags object with the given flags.
+ explicit FlagsTemplate(unsigned long flags) : _flags(flags) {}
+
+ unsigned long _flags;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+#include "gromacs/gmxlib/version.h"
+const char _gmx_ver_string[] = "VERSION @GMX_PROJECT_VERSION_STR@";
+const char _gmx_full_git_hash[] = "@GMX_GIT_HEAD_HASH@";
+const char _gmx_central_base_hash[] = "@GMX_GIT_REMOTE_HASH@";
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
\ No newline at end of file
.deps
.libs
+g_luck
+g_protonate
+g_x2top
+gmxcheck
+gmxdump
+grompp
+mdrun
+pdb2gmx
+tpbconv
pgutil.c
readir.c
readpull.c
+ readrot.c
resall.c
sorting.c
specbond.c
set(MDRUN_SOURCES
gctio.c ionize.c runner.c
do_gct.c repl_ex.c xutils.c
- md.c mdrun.c genalg.c md_openmm.c)
+ md.c mdrun.c genalg.c membed.c
+ md_openmm.c)
add_library(gmxpreprocess ${GMXPREPROCESS_SOURCES})
-target_link_libraries(gmxpreprocess md)
+target_link_libraries(gmxpreprocess libgromacs)
set_target_properties(gmxpreprocess PROPERTIES OUTPUT_NAME "gmxpreprocess${GMX_LIBS_SUFFIX}" SOVERSION ${SOVERSION} INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
add_library(fahcore ${MDRUN_SOURCES})
else(GMX_FAHCORE)
-list(APPEND GMX_EXTRA_LIBRARIES gmxpreprocess md)
+list(APPEND GMX_EXTRA_LIBRARIES gmxpreprocess libgromacs)
add_executable(grompp grompp.c)
target_link_libraries(grompp ${GMX_EXTRA_LIBRARIES})
pdb2top.c \
pgutil.c pgutil.h \
readir.c readir.h \
-readpull.c \
+readpull.c readrot.c \
resall.c \
sorting.c sorting.h \
specbond.c specbond.h \
g_x2top_SOURCES = g_x2top.c nm2type.c g_x2top.h
mdrun_SOURCES = \
- gctio.c \
+ gctio.c membed.c membed.h \
ionize.c ionize.h xmdrun.h \
do_gct.c repl_ex.c repl_ex.h \
xutils.c runner.c md.c mdrun.c \
{ efTOP, "-pp", "processed", ffOPTWR },
{ efTPX, "-o", NULL, ffWRITE },
{ efTRN, "-t", NULL, ffOPTRD },
- { efEDR, "-e", NULL, ffOPTRD }
+ { efEDR, "-e", NULL, ffOPTRD },
+ { efTRN, "-ref","rotref", ffOPTRW }
};
#define NFILE asize(fnm)
if (ir->ePull != epullNO)
set_pull_init(ir,sys,state.x,state.box,oenv,opts->pull_start);
+
+ if (ir->bRot)
+ {
+ set_reference_positions(ir->rot,sys,state.x,state.box,
+ opt2fn("-ref",NFILE,fnm),opt2bSet("-ref",NFILE,fnm),
+ wi);
+ }
/* reset_multinr(sys); */
#include "checkpoint.h"
#include "mtop_util.h"
#include "sighandler.h"
+#include "membed.h"
#ifdef GMX_LIB_MPI
#include <mpi.h>
t_mdatoms *mdatoms,
t_nrnb *nrnb,gmx_wallcycle_t wcycle,
gmx_edsam_t ed,t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
+ int repl_ex_nst,int repl_ex_seed,gmx_membed_t *membed,
real cpt_period,real max_hours,
const char *deviceOptions,
unsigned long Flags,
}
/* ####### END SET VARIABLES FOR NEXT ITERATION ###### */
+
+ if ( (membed!=NULL) && (!bLastStep) )
+ rescale_membed(step_rel,membed,state_global->x);
if (bRerunMD)
{
#include "genborn.h"
#include "string2.h"
#include "copyrite.h"
+#include "membed.h"
#ifdef GMX_THREADS
#include "tmpi.h"
t_nrnb *nrnb,gmx_wallcycle_t wcycle,
gmx_edsam_t ed,t_forcerec *fr,
int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
real cpt_period,real max_hours,
const char *deviceOptions,
unsigned long Flags,
t_nrnb *nrnb,gmx_wallcycle_t wcycle,
gmx_edsam_t ed,t_forcerec *fr,
int repl_ex_nst,int repl_ex_seed,
+ gmx_membed_t *membed,
real cpt_period,real max_hours,
const char *deviceOptions,
unsigned long Flags,
"appropriate options have been given. Currently under",
"investigation are: polarizability, and X-Ray bombardments.",
"[PAR]",
+ "The option [TT]-membed[dd] does what used to be g_membed, i.e. embed",
+ "a protein into a membrane. The data file should contain the options",
+ "that where passed to g_membed before. The [TT]-mn[tt] and [TT]-mp[tt]",
+ "both apply to this as well.",
+ "[PAR]",
"The option [TT]-pforce[tt] is useful when you suspect a simulation",
"crashes due to too large forces. With this option coordinates and",
"forces of atoms with a force larger than a certain value will",
{ efXVG, "-runav", "runaver", ffOPTWR },
{ efXVG, "-px", "pullx", ffOPTWR },
{ efXVG, "-pf", "pullf", ffOPTWR },
+ { efXVG, "-ro", "rotation", ffOPTWR },
+ { efLOG, "-ra", "rotangles",ffOPTWR },
+ { efLOG, "-rs", "rotslabs", ffOPTWR },
+ { efLOG, "-rt", "rottorque",ffOPTWR },
{ efMTX, "-mtx", "nm", ffOPTWR },
- { efNDX, "-dn", "dipole", ffOPTWR }
+ { efNDX, "-dn", "dipole", ffOPTWR },
+ { efDAT, "-membed", "membed", ffOPTRD },
+ { efTOP, "-mp", "membed", ffOPTRD },
+ { efNDX, "-mn", "membed", ffOPTRD }
};
#define NFILE asize(fnm)
--- /dev/null
+/*
+ * $Id: mdrun.c,v 1.139.2.9 2009/05/04 16:13:29 hess Exp $
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <stdlib.h>
+#include "typedefs.h"
+#include "smalloc.h"
+#include "sysstuff.h"
+#include "vec.h"
+#include "statutil.h"
+#include "macros.h"
+#include "copyrite.h"
+#include "main.h"
+#include "futil.h"
+#include "edsam.h"
+#include "index.h"
+#include "physics.h"
+#include "names.h"
+#include "mtop_util.h"
+#include "tpxio.h"
+#include "string2.h"
+#include "membed.h"
+#include "pbc.h"
+#include "readinp.h"
+
+typedef struct {
+ int id;
+ char *name;
+ int nr;
+ int natoms; /*nr of atoms per lipid*/
+ int mol1; /*id of the first lipid molecule*/
+ real area;
+} lip_t;
+
+typedef struct {
+ char *name;
+ t_block mem_at;
+ int *mol_id;
+ int nmol;
+ real lip_area;
+ real zmin;
+ real zmax;
+ real zmed;
+} mem_t;
+
+typedef struct {
+ int *mol;
+ int *block;
+ int nr;
+} rm_t;
+
+int search_string(char *s,int ng,char ***gn)
+{
+ int i;
+
+ for(i=0; (i<ng); i++)
+ if (gmx_strcasecmp(s,*gn[i]) == 0)
+ return i;
+
+ gmx_fatal(FARGS,"Group %s not found in indexfile.\nMaybe you have non-default groups in your mdp file, while not using the '-n' option of grompp.\nIn that case use the '-n' option.\n",s);
+
+ return -1;
+}
+
+int get_mol_id(int at,int nmblock,gmx_molblock_t *mblock, int *type, int *block)
+{
+ int mol_id=0;
+ int i;
+
+ for(i=0;i<nmblock;i++)
+ {
+ if(at<(mblock[i].nmol*mblock[i].natoms_mol))
+ {
+ mol_id+=at/mblock[i].natoms_mol;
+ *type = mblock[i].type;
+ *block = i;
+ return mol_id;
+ } else {
+ at-= mblock[i].nmol*mblock[i].natoms_mol;
+ mol_id+=mblock[i].nmol;
+ }
+ }
+
+ gmx_fatal(FARGS,"Something is wrong in mol ids, at %d, mol_id %d",at,mol_id);
+
+ return -1;
+}
+
+int get_block(int mol_id,int nmblock,gmx_molblock_t *mblock)
+{
+ int i;
+ int nmol=0;
+
+ for(i=0;i<nmblock;i++)
+ {
+ nmol+=mblock[i].nmol;
+ if(mol_id<nmol)
+ return i;
+ }
+
+ gmx_fatal(FARGS,"mol_id %d larger than total number of molecules %d.\n",mol_id,nmol);
+
+ return -1;
+}
+
+int get_tpr_version(const char *infile)
+{
+ char buf[STRLEN];
+ gmx_bool bDouble;
+ int precision,fver;
+ t_fileio *fio;
+
+ fio = open_tpx(infile,"r");
+ gmx_fio_checktype(fio);
+
+ precision = sizeof(real);
+
+ gmx_fio_do_string(fio,buf);
+ if (strncmp(buf,"VERSION",7))
+ gmx_fatal(FARGS,"Can not read file %s,\n"
+ " this file is from a Gromacs version which is older than 2.0\n"
+ " Make a new one with grompp or use a gro or pdb file, if possible",
+ gmx_fio_getname(fio));
+ gmx_fio_do_int(fio,precision);
+ bDouble = (precision == sizeof(double));
+ if ((precision != sizeof(float)) && !bDouble)
+ gmx_fatal(FARGS,"Unknown precision in file %s: real is %d bytes "
+ "instead of %d or %d",
+ gmx_fio_getname(fio),precision,sizeof(float),sizeof(double));
+ gmx_fio_setprecision(fio,bDouble);
+ fprintf(stderr,"Reading file %s, %s (%s precision)\n",
+ gmx_fio_getname(fio),buf,bDouble ? "double" : "single");
+
+ gmx_fio_do_int(fio,fver);
+
+ close_tpx(fio);
+
+ return fver;
+}
+
+int get_mtype_list(t_block *at, gmx_mtop_t *mtop, t_block *tlist)
+{
+ int i,j,nr,mol_id;
+ int type=0,block=0;
+ gmx_bool bNEW;
+
+ nr=0;
+ snew(tlist->index,at->nr);
+ for (i=0;i<at->nr;i++)
+ {
+ bNEW=TRUE;
+ mol_id = get_mol_id(at->index[i],mtop->nmolblock,mtop->molblock,&type,&block);
+ for(j=0;j<nr;j++)
+ {
+ if(tlist->index[j]==type)
+ bNEW=FALSE;
+ }
+ if(bNEW==TRUE)
+ {
+ tlist->index[nr]=type;
+ nr++;
+ }
+ }
+
+ srenew(tlist->index,nr);
+ return nr;
+}
+
+void check_types(t_block *ins_at,t_block *rest_at,gmx_mtop_t *mtop)
+{
+ t_block *ins_mtype,*rest_mtype;
+ int i,j;
+
+ snew(ins_mtype,1);
+ snew(rest_mtype,1);
+ ins_mtype->nr = get_mtype_list(ins_at , mtop, ins_mtype );
+ rest_mtype->nr = get_mtype_list(rest_at, mtop, rest_mtype);
+
+ for(i=0;i<ins_mtype->nr;i++)
+ {
+ for(j=0;j<rest_mtype->nr;j++)
+ {
+ if(ins_mtype->index[i]==rest_mtype->index[j])
+ gmx_fatal(FARGS,"Moleculetype %s is found both in the group to insert and the rest of the system.\n"
+ "1. Your *.ndx and *.top do not match\n"
+ "2. You are inserting some molecules of type %s (for example xray-solvent), while\n"
+ "the same moleculetype is also used in the rest of the system (solvent box). Because\n"
+ "we need to exclude all interactions between the atoms in the group to\n"
+ "insert, the same moleculetype can not be used in both groups. Change the\n"
+ "moleculetype of the molecules %s in the inserted group. Do not forget to provide\n"
+ "an appropriate *.itp file",*(mtop->moltype[rest_mtype->index[j]].name),
+ *(mtop->moltype[rest_mtype->index[j]].name),*(mtop->moltype[rest_mtype->index[j]].name));
+ }
+ }
+
+ sfree(ins_mtype->index);
+ sfree(rest_mtype->index);
+ sfree(ins_mtype);
+ sfree(rest_mtype);
+}
+
+void get_input(const char *membed_input, real *xy_fac, real *xy_max, real *z_fac, real *z_max,
+ int *it_xy, int *it_z, real *probe_rad, int *low_up_rm, int *maxwarn,
+ int *pieces, gmx_bool *bALLOW_ASYMMETRY)
+{
+ warninp_t wi;
+ t_inpfile *inp;
+ int ninp;
+
+ wi = init_warning(TRUE,0);
+
+ inp = read_inpfile(membed_input, &ninp, NULL, wi);
+ ITYPE ("nxy", *it_xy, 1000);
+ ITYPE ("nz", *it_z, 0);
+ RTYPE ("xyinit", *xy_fac, 0.5);
+ RTYPE ("xyend", *xy_max, 1.0);
+ RTYPE ("zinit", *z_fac, 1.0);
+ RTYPE ("zend", *z_max, 1.0);
+ RTYPE ("rad", *probe_rad, 0.22);
+ ITYPE ("ndiff", *low_up_rm, 0);
+ ITYPE ("maxwarn", *maxwarn, 0);
+ ITYPE ("pieces", *pieces, 1);
+ EETYPE("asymmetry", *bALLOW_ASYMMETRY, yesno_names);
+
+ write_inpfile(membed_input,ninp,inp,FALSE,wi);
+}
+
+int init_ins_at(t_block *ins_at,t_block *rest_at,t_state *state, pos_ins_t *pos_ins,gmx_groups_t *groups,int ins_grp_id, real xy_max)
+{
+ int i,gid,c=0;
+ real x,xmin,xmax,y,ymin,ymax,z,zmin,zmax;
+
+ snew(rest_at->index,state->natoms);
+
+ xmin=xmax=state->x[ins_at->index[0]][XX];
+ ymin=ymax=state->x[ins_at->index[0]][YY];
+ zmin=zmax=state->x[ins_at->index[0]][ZZ];
+
+ for(i=0;i<state->natoms;i++)
+ {
+ gid = groups->grpnr[egcFREEZE][i];
+ if(groups->grps[egcFREEZE].nm_ind[gid]==ins_grp_id)
+ {
+ x=state->x[i][XX];
+ if (x<xmin) xmin=x;
+ if (x>xmax) xmax=x;
+ y=state->x[i][YY];
+ if (y<ymin) ymin=y;
+ if (y>ymax) ymax=y;
+ z=state->x[i][ZZ];
+ if (z<zmin) zmin=z;
+ if (z>zmax) zmax=z;
+ } else {
+ rest_at->index[c]=i;
+ c++;
+ }
+ }
+
+ rest_at->nr=c;
+ srenew(rest_at->index,c);
+
+ if(xy_max>1.000001)
+ {
+ pos_ins->xmin[XX]=xmin-((xmax-xmin)*xy_max-(xmax-xmin))/2;
+ pos_ins->xmin[YY]=ymin-((ymax-ymin)*xy_max-(ymax-ymin))/2;
+
+ pos_ins->xmax[XX]=xmax+((xmax-xmin)*xy_max-(xmax-xmin))/2;
+ pos_ins->xmax[YY]=ymax+((ymax-ymin)*xy_max-(ymax-ymin))/2;
+ } else {
+ pos_ins->xmin[XX]=xmin;
+ pos_ins->xmin[YY]=ymin;
+
+ pos_ins->xmax[XX]=xmax;
+ pos_ins->xmax[YY]=ymax;
+ }
+
+ /* 6.0 is estimated thickness of bilayer */
+ if( (zmax-zmin) < 6.0 )
+ {
+ pos_ins->xmin[ZZ]=zmin+(zmax-zmin)/2.0-3.0;
+ pos_ins->xmax[ZZ]=zmin+(zmax-zmin)/2.0+3.0;
+ } else {
+ pos_ins->xmin[ZZ]=zmin;
+ pos_ins->xmax[ZZ]=zmax;
+ }
+
+ return c;
+}
+
+real est_prot_area(pos_ins_t *pos_ins,rvec *r,t_block *ins_at, mem_t *mem_p)
+{
+ real x,y,dx=0.15,dy=0.15,area=0.0;
+ real add;
+ int c,at;
+
+ for(x=pos_ins->xmin[XX];x<pos_ins->xmax[XX];x+=dx)
+ {
+ for(y=pos_ins->xmin[YY];y<pos_ins->xmax[YY];y+=dy)
+ {
+ c=0;
+ add=0.0;
+ do
+ {
+ at=ins_at->index[c];
+ if ( (r[at][XX]>=x) && (r[at][XX]<x+dx) &&
+ (r[at][YY]>=y) && (r[at][YY]<y+dy) &&
+ (r[at][ZZ]>mem_p->zmin+1.0) && (r[at][ZZ]<mem_p->zmax-1.0) )
+ add=1.0;
+ c++;
+ } while ( (c<ins_at->nr) && (add<0.5) );
+ area+=add;
+ }
+ }
+ area=area*dx*dy;
+
+ return area;
+}
+
+void init_lip(matrix box, gmx_mtop_t *mtop, lip_t *lip)
+{
+ int i;
+ real mem_area;
+ int mol1=0;
+
+ mem_area = box[XX][XX]*box[YY][YY]-box[XX][YY]*box[YY][XX];
+ for(i=0;i<mtop->nmolblock;i++)
+ {
+ if(mtop->molblock[i].type == lip->id)
+ {
+ lip->nr=mtop->molblock[i].nmol;
+ lip->natoms=mtop->molblock[i].natoms_mol;
+ }
+ }
+ lip->area=2.0*mem_area/(double)lip->nr;
+
+ for (i=0;i<lip->id;i++)
+ mol1+=mtop->molblock[i].nmol;
+ lip->mol1=mol1;
+}
+
+int init_mem_at(mem_t *mem_p, gmx_mtop_t *mtop, rvec *r, matrix box, pos_ins_t *pos_ins)
+{
+ int i,j,at,mol,nmol,nmolbox,count;
+ t_block *mem_a;
+ real z,zmin,zmax,mem_area;
+ gmx_bool bNew;
+ atom_id *mol_id;
+ int type=0,block=0;
+
+ nmol=count=0;
+ mem_a=&(mem_p->mem_at);
+ snew(mol_id,mem_a->nr);
+/* snew(index,mem_a->nr); */
+ zmin=pos_ins->xmax[ZZ];
+ zmax=pos_ins->xmin[ZZ];
+ for(i=0;i<mem_a->nr;i++)
+ {
+ at=mem_a->index[i];
+ if( (r[at][XX]>pos_ins->xmin[XX]) && (r[at][XX]<pos_ins->xmax[XX]) &&
+ (r[at][YY]>pos_ins->xmin[YY]) && (r[at][YY]<pos_ins->xmax[YY]) &&
+ (r[at][ZZ]>pos_ins->xmin[ZZ]) && (r[at][ZZ]<pos_ins->xmax[ZZ]) )
+ {
+ mol = get_mol_id(at,mtop->nmolblock,mtop->molblock,&type,&block);
+
+ bNew=TRUE;
+ for(j=0;j<nmol;j++)
+ if(mol == mol_id[j])
+ bNew=FALSE;
+
+ if(bNew)
+ {
+ mol_id[nmol]=mol;
+ nmol++;
+ }
+
+ z=r[at][ZZ];
+ if(z<zmin) zmin=z;
+ if(z>zmax) zmax=z;
+
+/* index[count]=at;*/
+ count++;
+ }
+ }
+
+ mem_p->nmol=nmol;
+ srenew(mol_id,nmol);
+ mem_p->mol_id=mol_id;
+/* srenew(index,count);*/
+/* mem_p->mem_at.nr=count;*/
+/* sfree(mem_p->mem_at.index);*/
+/* mem_p->mem_at.index=index;*/
+
+ if((zmax-zmin)>(box[ZZ][ZZ]-0.5))
+ gmx_fatal(FARGS,"Something is wrong with your membrane. Max and min z values are %f and %f.\n"
+ "Maybe your membrane is not centered in the box, but located at the box edge in the z-direction,\n"
+ "so that one membrane is distributed over two periodic box images. Another possibility is that\n"
+ "your water layer is not thick enough.\n",zmax,zmin);
+ mem_p->zmin=zmin;
+ mem_p->zmax=zmax;
+ mem_p->zmed=(zmax-zmin)/2+zmin;
+
+ /*number of membrane molecules in protein box*/
+ nmolbox = count/mtop->molblock[block].natoms_mol;
+ /*mem_area = box[XX][XX]*box[YY][YY]-box[XX][YY]*box[YY][XX];
+ mem_p->lip_area = 2.0*mem_area/(double)mem_p->nmol;*/
+ mem_area = (pos_ins->xmax[XX]-pos_ins->xmin[XX])*(pos_ins->xmax[YY]-pos_ins->xmin[YY]);
+ mem_p->lip_area = 2.0*mem_area/(double)nmolbox;
+
+ return mem_p->mem_at.nr;
+}
+
+void init_resize(t_block *ins_at,rvec *r_ins,pos_ins_t *pos_ins,mem_t *mem_p,rvec *r, gmx_bool bALLOW_ASYMMETRY)
+{
+ int i,j,at,c,outsidesum,gctr=0;
+ int idxsum=0;
+
+ /*sanity check*/
+ for (i=0;i<pos_ins->pieces;i++)
+ idxsum+=pos_ins->nidx[i];
+ if (idxsum!=ins_at->nr)
+ gmx_fatal(FARGS,"Piecewise sum of inserted atoms not same as size of group selected to insert.");
+
+ snew(pos_ins->geom_cent,pos_ins->pieces);
+ for (i=0;i<pos_ins->pieces;i++)
+ {
+ c=0;
+ outsidesum=0;
+ for(j=0;j<DIM;j++)
+ pos_ins->geom_cent[i][j]=0;
+
+ for(j=0;j<DIM;j++)
+ pos_ins->geom_cent[i][j]=0;
+ for (j=0;j<pos_ins->nidx[i];j++)
+ {
+ at=pos_ins->subindex[i][j];
+ copy_rvec(r[at],r_ins[gctr]);
+ if( (r_ins[gctr][ZZ]<mem_p->zmax) && (r_ins[gctr][ZZ]>mem_p->zmin) )
+ {
+ rvec_inc(pos_ins->geom_cent[i],r_ins[gctr]);
+ c++;
+ }
+ else
+ outsidesum++;
+ gctr++;
+ }
+ if (c>0)
+ svmul(1/(double)c,pos_ins->geom_cent[i],pos_ins->geom_cent[i]);
+ if (!bALLOW_ASYMMETRY)
+ pos_ins->geom_cent[i][ZZ]=mem_p->zmed;
+
+ fprintf(stderr,"Embedding piece %d with center of geometry: %f %f %f\n",i,pos_ins->geom_cent[i][XX],pos_ins->geom_cent[i][YY],pos_ins->geom_cent[i][ZZ]);
+ }
+ fprintf(stderr,"\n");
+}
+
+void resize(rvec *r_ins, rvec *r, pos_ins_t *pos_ins,rvec fac)
+{
+ int i,j,k,at,c=0;
+ for (k=0;k<pos_ins->pieces;k++)
+ for(i=0;i<pos_ins->nidx[k];i++)
+ {
+ at=pos_ins->subindex[k][i];
+ for(j=0;j<DIM;j++)
+ r[at][j]=pos_ins->geom_cent[k][j]+fac[j]*(r_ins[c][j]-pos_ins->geom_cent[k][j]);
+ c++;
+ }
+}
+
+int gen_rm_list(rm_t *rm_p,t_block *ins_at,t_block *rest_at,t_pbc *pbc, gmx_mtop_t *mtop,
+ rvec *r, rvec *r_ins, mem_t *mem_p, pos_ins_t *pos_ins, real probe_rad, int low_up_rm, gmx_bool bALLOW_ASYMMETRY)
+{
+ int i,j,k,l,at,at2,mol_id;
+ int type=0,block=0;
+ int nrm,nupper,nlower;
+ real r_min_rad,z_lip,min_norm;
+ gmx_bool bRM;
+ rvec dr,dr_tmp;
+ real *dist;
+ int *order;
+
+ r_min_rad=probe_rad*probe_rad;
+ snew(rm_p->mol,mtop->mols.nr);
+ snew(rm_p->block,mtop->mols.nr);
+ nrm=nupper=0;
+ nlower=low_up_rm;
+ for(i=0;i<ins_at->nr;i++)
+ {
+ at=ins_at->index[i];
+ for(j=0;j<rest_at->nr;j++)
+ {
+ at2=rest_at->index[j];
+ pbc_dx(pbc,r[at],r[at2],dr);
+
+ if(norm2(dr)<r_min_rad)
+ {
+ mol_id = get_mol_id(at2,mtop->nmolblock,mtop->molblock,&type,&block);
+ bRM=TRUE;
+ for(l=0;l<nrm;l++)
+ if(rm_p->mol[l]==mol_id)
+ bRM=FALSE;
+ if(bRM)
+ {
+ /*fprintf(stderr,"%d wordt toegevoegd\n",mol_id);*/
+ rm_p->mol[nrm]=mol_id;
+ rm_p->block[nrm]=block;
+ nrm++;
+ z_lip=0.0;
+ for(l=0;l<mem_p->nmol;l++)
+ {
+ if(mol_id==mem_p->mol_id[l])
+ {
+ for(k=mtop->mols.index[mol_id];k<mtop->mols.index[mol_id+1];k++)
+ z_lip+=r[k][ZZ];
+ z_lip/=mtop->molblock[block].natoms_mol;
+ if(z_lip<mem_p->zmed)
+ nlower++;
+ else
+ nupper++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*make sure equal number of lipids from upper and lower layer are removed */
+ if( (nupper!=nlower) && (!bALLOW_ASYMMETRY) )
+ {
+ snew(dist,mem_p->nmol);
+ snew(order,mem_p->nmol);
+ for(i=0;i<mem_p->nmol;i++)
+ {
+ at = mtop->mols.index[mem_p->mol_id[i]];
+ pbc_dx(pbc,r[at],pos_ins->geom_cent[0],dr);
+ if (pos_ins->pieces>1)
+ {
+ /*minimum dr value*/
+ min_norm=norm2(dr);
+ for (k=1;k<pos_ins->pieces;k++)
+ {
+ pbc_dx(pbc,r[at],pos_ins->geom_cent[k],dr_tmp);
+ if (norm2(dr_tmp) < min_norm)
+ {
+ min_norm=norm2(dr_tmp);
+ copy_rvec(dr_tmp,dr);
+ }
+ }
+ }
+ dist[i]=dr[XX]*dr[XX]+dr[YY]*dr[YY];
+ j=i-1;
+ while (j>=0 && dist[i]<dist[order[j]])
+ {
+ order[j+1]=order[j];
+ j--;
+ }
+ order[j+1]=i;
+ }
+
+ i=0;
+ while(nupper!=nlower)
+ {
+ mol_id=mem_p->mol_id[order[i]];
+ block=get_block(mol_id,mtop->nmolblock,mtop->molblock);
+
+ bRM=TRUE;
+ for(l=0;l<nrm;l++)
+ if(rm_p->mol[l]==mol_id)
+ bRM=FALSE;
+ if(bRM)
+ {
+ z_lip=0;
+ for(k=mtop->mols.index[mol_id];k<mtop->mols.index[mol_id+1];k++)
+ z_lip+=r[k][ZZ];
+ z_lip/=mtop->molblock[block].natoms_mol;
+ if(nupper>nlower && z_lip<mem_p->zmed)
+ {
+ rm_p->mol[nrm]=mol_id;
+ rm_p->block[nrm]=block;
+ nrm++;
+ nlower++;
+ }
+ else if (nupper<nlower && z_lip>mem_p->zmed)
+ {
+ rm_p->mol[nrm]=mol_id;
+ rm_p->block[nrm]=block;
+ nrm++;
+ nupper++;
+ }
+ }
+ i++;
+
+ if(i>mem_p->nmol)
+ gmx_fatal(FARGS,"Trying to remove more lipid molecules than there are in the membrane");
+ }
+ sfree(dist);
+ sfree(order);
+ }
+
+ rm_p->nr=nrm;
+ srenew(rm_p->mol,nrm);
+ srenew(rm_p->block,nrm);
+
+ return nupper+nlower;
+}
+
+void rm_group(t_inputrec *ir, gmx_groups_t *groups, gmx_mtop_t *mtop, rm_t *rm_p, t_state *state, t_block *ins_at, pos_ins_t *pos_ins)
+{
+ int i,j,k,n,rm,mol_id,at,block;
+ rvec *x_tmp,*v_tmp;
+ atom_id *list,*new_mols;
+ unsigned char *new_egrp[egcNR];
+ gmx_bool bRM;
+
+ snew(list,state->natoms);
+ n=0;
+ for(i=0;i<rm_p->nr;i++)
+ {
+ mol_id=rm_p->mol[i];
+ at=mtop->mols.index[mol_id];
+ block =rm_p->block[i];
+ mtop->molblock[block].nmol--;
+ for(j=0;j<mtop->molblock[block].natoms_mol;j++)
+ {
+ list[n]=at+j;
+ n++;
+ }
+
+ mtop->mols.index[mol_id]=-1;
+ }
+
+ mtop->mols.nr-=rm_p->nr;
+ mtop->mols.nalloc_index-=rm_p->nr;
+ snew(new_mols,mtop->mols.nr);
+ for(i=0;i<mtop->mols.nr+rm_p->nr;i++)
+ {
+ j=0;
+ if(mtop->mols.index[i]!=-1)
+ {
+ new_mols[j]=mtop->mols.index[i];
+ j++;
+ }
+ }
+ sfree(mtop->mols.index);
+ mtop->mols.index=new_mols;
+
+
+ mtop->natoms-=n;
+ state->natoms-=n;
+ state->nalloc=state->natoms;
+ snew(x_tmp,state->nalloc);
+ snew(v_tmp,state->nalloc);
+
+ for(i=0;i<egcNR;i++)
+ {
+ if(groups->grpnr[i]!=NULL)
+ {
+ groups->ngrpnr[i]=state->natoms;
+ snew(new_egrp[i],state->natoms);
+ }
+ }
+
+ rm=0;
+ for (i=0;i<state->natoms+n;i++)
+ {
+ bRM=FALSE;
+ for(j=0;j<n;j++)
+ {
+ if(i==list[j])
+ {
+ bRM=TRUE;
+ rm++;
+ }
+ }
+
+ if(!bRM)
+ {
+ for(j=0;j<egcNR;j++)
+ {
+ if(groups->grpnr[j]!=NULL)
+ {
+ new_egrp[j][i-rm]=groups->grpnr[j][i];
+ }
+ }
+ copy_rvec(state->x[i],x_tmp[i-rm]);
+ copy_rvec(state->v[i],v_tmp[i-rm]);
+ for(j=0;j<ins_at->nr;j++)
+ {
+ if (i==ins_at->index[j])
+ ins_at->index[j]=i-rm;
+ }
+ for(j=0;j<pos_ins->pieces;j++)
+ {
+ for(k=0;k<pos_ins->nidx[j];k++)
+ {
+ if (i==pos_ins->subindex[j][k])
+ pos_ins->subindex[j][k]=i-rm;
+ }
+ }
+ }
+ }
+ sfree(state->x);
+ state->x=x_tmp;
+ sfree(state->v);
+ state->v=v_tmp;
+
+ for(i=0;i<egcNR;i++)
+ {
+ if(groups->grpnr[i]!=NULL)
+ {
+ sfree(groups->grpnr[i]);
+ groups->grpnr[i]=new_egrp[i];
+ }
+ }
+}
+
+int rm_bonded(t_block *ins_at, gmx_mtop_t *mtop)
+{
+ int i,j,m;
+ int type,natom,nmol,at,atom1=0,rm_at=0;
+ gmx_bool *bRM,bINS;
+ /*this routine lives dangerously by assuming that all molecules of a given type are in order in the structure*/
+ /*this routine does not live as dangerously as it seems. There is namely a check in mdrunner_membed to make
+ *sure that g_membed exits with a warning when there are molecules of the same type not in the
+ *ins_at index group. MGWolf 050710 */
+
+
+ snew(bRM,mtop->nmoltype);
+ for (i=0;i<mtop->nmoltype;i++)
+ {
+ bRM[i]=TRUE;
+ }
+
+ for (i=0;i<mtop->nmolblock;i++)
+ {
+ /*loop over molecule blocks*/
+ type =mtop->molblock[i].type;
+ natom =mtop->molblock[i].natoms_mol;
+ nmol =mtop->molblock[i].nmol;
+
+ for(j=0;j<natom*nmol && bRM[type]==TRUE;j++)
+ {
+ /*loop over atoms in the block*/
+ at=j+atom1; /*atom index = block index + offset*/
+ bINS=FALSE;
+
+ for (m=0;(m<ins_at->nr) && (bINS==FALSE);m++)
+ {
+ /*loop over atoms in insertion index group to determine if we're inserting one*/
+ if(at==ins_at->index[m])
+ {
+ bINS=TRUE;
+ }
+ }
+ bRM[type]=bINS;
+ }
+ atom1+=natom*nmol; /*update offset*/
+ if(bRM[type])
+ {
+ rm_at+=natom*nmol; /*increment bonded removal counter by # atoms in block*/
+ }
+ }
+
+ for(i=0;i<mtop->nmoltype;i++)
+ {
+ if(bRM[i])
+ {
+ for(j=0;j<F_LJ;j++)
+ {
+ mtop->moltype[i].ilist[j].nr=0;
+ }
+ for(j=F_POSRES;j<=F_VSITEN;j++)
+ {
+ mtop->moltype[i].ilist[j].nr=0;
+ }
+ }
+ }
+ sfree(bRM);
+
+ return rm_at;
+}
+
+void top_update(const char *topfile, char *ins, rm_t *rm_p, gmx_mtop_t *mtop)
+{
+#define TEMP_FILENM "temp.top"
+ int bMolecules=0;
+ FILE *fpin,*fpout;
+ char buf[STRLEN],buf2[STRLEN],*temp;
+ int i,*nmol_rm,nmol,line;
+
+ fpin = ffopen(topfile,"r");
+ fpout = ffopen(TEMP_FILENM,"w");
+
+ snew(nmol_rm,mtop->nmoltype);
+ for(i=0;i<rm_p->nr;i++)
+ nmol_rm[rm_p->block[i]]++;
+
+ line=0;
+ while(fgets(buf,STRLEN,fpin))
+ {
+ line++;
+ if(buf[0]!=';')
+ {
+ strcpy(buf2,buf);
+ if ((temp=strchr(buf2,'\n')) != NULL)
+ temp[0]='\0';
+ ltrim(buf2);
+
+ if (buf2[0]=='[')
+ {
+ buf2[0]=' ';
+ if ((temp=strchr(buf2,'\n')) != NULL)
+ temp[0]='\0';
+ rtrim(buf2);
+ if (buf2[strlen(buf2)-1]==']')
+ {
+ buf2[strlen(buf2)-1]='\0';
+ ltrim(buf2);
+ rtrim(buf2);
+ if (gmx_strcasecmp(buf2,"molecules")==0)
+ bMolecules=1;
+ }
+ fprintf(fpout,"%s",buf);
+ } else if (bMolecules==1)
+ {
+ for(i=0;i<mtop->nmolblock;i++)
+ {
+ nmol=mtop->molblock[i].nmol;
+ sprintf(buf,"%-15s %5d\n",*(mtop->moltype[mtop->molblock[i].type].name),nmol);
+ fprintf(fpout,"%s",buf);
+ }
+ bMolecules=2;
+ } else if (bMolecules==2)
+ {
+ /* print nothing */
+ } else
+ {
+ fprintf(fpout,"%s",buf);
+ }
+ } else
+ {
+ fprintf(fpout,"%s",buf);
+ }
+ }
+
+ fclose(fpout);
+ /* use ffopen to generate backup of topinout */
+ fpout=ffopen(topfile,"w");
+ fclose(fpout);
+ rename(TEMP_FILENM,topfile);
+#undef TEMP_FILENM
+}
+
+void rescale_membed(int step_rel, gmx_membed_t *membed, rvec *x)
+{
+ /* Set new positions for the group to embed */
+ if(step_rel<=membed->it_xy)
+ {
+ membed->fac[0]+=membed->xy_step;
+ membed->fac[1]+=membed->xy_step;
+ } else if (step_rel<=(membed->it_xy+membed->it_z))
+ {
+ membed->fac[2]+=membed->z_step;
+ }
+ resize(membed->r_ins,x,membed->pos_ins,membed->fac);
+}
+
+void init_membed(FILE *fplog, gmx_membed_t *membed, int nfile, const t_filenm fnm[], gmx_mtop_t *mtop, t_inputrec *inputrec, t_state *state, t_commrec *cr,real *cpt)
+{
+ char *ins;
+ int i,rm_bonded_at,fr_id,fr_i=0,tmp_id,warn=0;
+ int ng,j,max_lip_rm,ins_grp_id,ins_nat,mem_nat,ntype,lip_rm,tpr_version;
+ real prot_area;
+ rvec *r_ins=NULL;
+ t_block *ins_at,*rest_at;
+ pos_ins_t *pos_ins;
+ mem_t *mem_p;
+ rm_t *rm_p;
+ gmx_groups_t *groups;
+ gmx_bool bExcl=FALSE;
+ t_atoms atoms;
+ t_pbc *pbc;
+ char **piecename=NULL;
+
+ /* input variables */
+ const char *membed_input;
+ real xy_fac = 0.5;
+ real xy_max = 1.0;
+ real z_fac = 1.0;
+ real z_max = 1.0;
+ int it_xy = 1000;
+ int it_z = 0;
+ real probe_rad = 0.22;
+ int low_up_rm = 0;
+ int maxwarn=0;
+ int pieces=1;
+ gmx_bool bALLOW_ASYMMETRY=FALSE;
+
+ snew(ins_at,1);
+ snew(pos_ins,1);
+
+ if(MASTER(cr))
+ {
+ /* get input data out membed file */
+ membed_input = opt2fn("-membed",nfile,fnm);
+ get_input(membed_input,&xy_fac,&xy_max,&z_fac,&z_max,&it_xy,&it_z,&probe_rad,&low_up_rm,&maxwarn,&pieces,&bALLOW_ASYMMETRY);
+
+ tpr_version = get_tpr_version(ftp2fn(efTPX,nfile,fnm));
+ if (tpr_version<58)
+ gmx_fatal(FARGS,"Version of *.tpr file to old (%d). Rerun grompp with gromacs VERSION 4.0.3 or newer.\n",tpr_version);
+
+ if( !EI_DYNAMICS(inputrec->eI) )
+ gmx_input("Change integrator to a dynamics integrator in mdp file (e.g. md or sd).");
+
+ if(PAR(cr))
+ gmx_input("Sorry, parallel g_membed is not yet fully functional.");
+
+#ifdef GMX_OPENMM
+ gmx_input("Sorry, g_membed does not work with openmm.");
+#endif
+
+ if(*cpt>=0)
+ {
+ fprintf(stderr,"\nSetting -cpt to -1, because embedding cannot be restarted from cpt-files.\n");
+ *cpt=-1;
+ }
+ groups=&(mtop->groups);
+
+ atoms=gmx_mtop_global_atoms(mtop);
+ snew(mem_p,1);
+ fprintf(stderr,"\nSelect a group to embed in the membrane:\n");
+ get_index(&atoms,opt2fn_null("-mn",nfile,fnm),1,&(ins_at->nr),&(ins_at->index),&ins);
+ ins_grp_id = search_string(ins,groups->ngrpname,(groups->grpname));
+ fprintf(stderr,"\nSelect a group to embed %s into (e.g. the membrane):\n",ins);
+ get_index(&atoms,opt2fn_null("-mn",nfile,fnm),1,&(mem_p->mem_at.nr),&(mem_p->mem_at.index),&(mem_p->name));
+
+ pos_ins->pieces=pieces;
+ snew(pos_ins->nidx,pieces);
+ snew(pos_ins->subindex,pieces);
+ snew(piecename,pieces);
+ if (pieces>1)
+ {
+ fprintf(stderr,"\nSelect pieces to embed:\n");
+ get_index(&atoms,opt2fn_null("-mn",nfile,fnm),pieces,pos_ins->nidx,pos_ins->subindex,piecename);
+ }
+ else
+ {
+ /*use whole embedded group*/
+ snew(pos_ins->nidx,1);
+ snew(pos_ins->subindex,1);
+ pos_ins->nidx[0]=ins_at->nr;
+ pos_ins->subindex[0]=ins_at->index;
+ }
+
+ if(probe_rad<0.2199999)
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d:\nA probe radius (-rad) smaller than 0.2 can result in overlap between waters "
+ "and the group to embed, which will result in Lincs errors etc.\nIf you are sure, you can increase maxwarn.\n\n",warn);
+ }
+
+ if(xy_fac<0.09999999)
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d:\nThe initial size of %s is probably too smal.\n"
+ "If you are sure, you can increase maxwarn.\n\n",warn,ins);
+ }
+
+ if(it_xy<1000)
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d;\nThe number of steps used to grow the xy-coordinates of %s (%d) is probably too small.\n"
+ "Increase -nxy or, if you are sure, you can increase maxwarn.\n\n",warn,ins,it_xy);
+ }
+
+ if( (it_z<100) && ( z_fac<0.99999999 || z_fac>1.0000001) )
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d;\nThe number of steps used to grow the z-coordinate of %s (%d) is probably too small.\n"
+ "Increase -nz or, if you are sure, you can increase maxwarn.\n\n",warn,ins,it_z);
+ }
+
+ if(it_xy+it_z>inputrec->nsteps)
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d:\nThe number of growth steps (-nxy + -nz) is larger than the number of steps in the tpr.\n"
+ "If you are sure, you can increase maxwarn.\n\n",warn);
+ }
+
+ fr_id=-1;
+ if( inputrec->opts.ngfrz==1)
+ gmx_fatal(FARGS,"You did not specify \"%s\" as a freezegroup.",ins);
+ for(i=0;i<inputrec->opts.ngfrz;i++)
+ {
+ tmp_id = mtop->groups.grps[egcFREEZE].nm_ind[i];
+ if(ins_grp_id==tmp_id)
+ {
+ fr_id=tmp_id;
+ fr_i=i;
+ }
+ }
+ if (fr_id == -1 )
+ gmx_fatal(FARGS,"\"%s\" not as freezegroup defined in the mdp-file.",ins);
+
+ for(i=0;i<DIM;i++)
+ if( inputrec->opts.nFreeze[fr_i][i] != 1)
+ gmx_fatal(FARGS,"freeze dimensions for %s are not Y Y Y\n",ins);
+
+ ng = groups->grps[egcENER].nr;
+ if (ng == 1)
+ gmx_input("No energy groups defined. This is necessary for energy exclusion in the freeze group");
+
+ for(i=0;i<ng;i++)
+ {
+ for(j=0;j<ng;j++)
+ {
+ if (inputrec->opts.egp_flags[ng*i+j] == EGP_EXCL)
+ {
+ bExcl = TRUE;
+ if ( (groups->grps[egcENER].nm_ind[i] != ins_grp_id) || (groups->grps[egcENER].nm_ind[j] != ins_grp_id) )
+ gmx_fatal(FARGS,"Energy exclusions \"%s\" and \"%s\" do not match the group to embed \"%s\"",
+ *groups->grpname[groups->grps[egcENER].nm_ind[i]],
+ *groups->grpname[groups->grps[egcENER].nm_ind[j]],ins);
+ }
+ }
+ }
+ if (!bExcl)
+ gmx_input("No energy exclusion groups defined. This is necessary for energy exclusion in the freeze group");
+
+ /* Guess the area the protein will occupy in the membrane plane Calculate area per lipid*/
+ snew(rest_at,1);
+ ins_nat = init_ins_at(ins_at,rest_at,state,pos_ins,groups,ins_grp_id,xy_max);
+ /* Check moleculetypes in insertion group */
+ check_types(ins_at,rest_at,mtop);
+
+ mem_nat = init_mem_at(mem_p,mtop,state->x,state->box,pos_ins);
+
+ prot_area = est_prot_area(pos_ins,state->x,ins_at,mem_p);
+ if ( (prot_area>7.5) && ( (state->box[XX][XX]*state->box[YY][YY]-state->box[XX][YY]*state->box[YY][XX])<50) )
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d:\nThe xy-area is very small compared to the area of the protein.\n"
+ "This might cause pressure problems during the growth phase. Just try with\n"
+ "current setup (-maxwarn + 1), but if pressure problems occur, lower the\n"
+ "compressibility in the mdp-file or use no pressure coupling at all.\n\n",warn);
+ }
+ if(warn>maxwarn)
+ gmx_fatal(FARGS,"Too many warnings.\n");
+
+ printf("The estimated area of the protein in the membrane is %.3f nm^2\n",prot_area);
+ printf("\nThere are %d lipids in the membrane part that overlaps the protein.\nThe area per lipid is %.4f nm^2.\n",mem_p->nmol,mem_p->lip_area);
+
+ /* Maximum number of lipids to be removed*/
+ max_lip_rm=(int)(2*prot_area/mem_p->lip_area);
+ printf("Maximum number of lipids that will be removed is %d.\n",max_lip_rm);
+
+ printf("\nWill resize the protein by a factor of %.3f in the xy plane and %.3f in the z direction.\n"
+ "This resizing will be done with respect to the geometrical center of all protein atoms\n"
+ "that span the membrane region, i.e. z between %.3f and %.3f\n\n",xy_fac,z_fac,mem_p->zmin,mem_p->zmax);
+
+ /* resize the protein by xy and by z if necessary*/
+ snew(r_ins,ins_at->nr);
+ init_resize(ins_at,r_ins,pos_ins,mem_p,state->x,bALLOW_ASYMMETRY);
+ membed->fac[0]=membed->fac[1]=xy_fac;
+ membed->fac[2]=z_fac;
+
+ membed->xy_step =(xy_max-xy_fac)/(double)(it_xy);
+ membed->z_step =(z_max-z_fac)/(double)(it_z-1);
+
+ resize(r_ins,state->x,pos_ins,membed->fac);
+
+ /* remove overlapping lipids and water from the membrane box*/
+ /*mark molecules to be removed*/
+ snew(pbc,1);
+ set_pbc(pbc,inputrec->ePBC,state->box);
+
+ snew(rm_p,1);
+ lip_rm = gen_rm_list(rm_p,ins_at,rest_at,pbc,mtop,state->x, r_ins, mem_p,pos_ins,probe_rad,low_up_rm,bALLOW_ASYMMETRY);
+ lip_rm -= low_up_rm;
+
+ if(fplog)
+ for(i=0;i<rm_p->nr;i++)
+ fprintf(fplog,"rm mol %d\n",rm_p->mol[i]);
+
+ for(i=0;i<mtop->nmolblock;i++)
+ {
+ ntype=0;
+ for(j=0;j<rm_p->nr;j++)
+ if(rm_p->block[j]==i)
+ ntype++;
+ printf("Will remove %d %s molecules\n",ntype,*(mtop->moltype[mtop->molblock[i].type].name));
+ }
+
+ if(lip_rm>max_lip_rm)
+ {
+ warn++;
+ fprintf(stderr,"\nWarning %d:\nTrying to remove a larger lipid area than the estimated protein area\n"
+ "Try making the -xyinit resize factor smaller.\n\n",warn);
+ }
+
+ /*remove all lipids and waters overlapping and update all important structures*/
+ rm_group(inputrec,groups,mtop,rm_p,state,ins_at,pos_ins);
+
+ rm_bonded_at = rm_bonded(ins_at,mtop);
+ if (rm_bonded_at != ins_at->nr)
+ {
+ fprintf(stderr,"Warning: The number of atoms for which the bonded interactions are removed is %d, "
+ "while %d atoms are embedded. Make sure that the atoms to be embedded are not in the same"
+ "molecule type as atoms that are not to be embedded.\n",rm_bonded_at,ins_at->nr);
+ }
+
+ if(warn>maxwarn)
+ gmx_fatal(FARGS,"Too many warnings.\nIf you are sure these warnings are harmless, you can increase -maxwarn");
+
+ if (ftp2bSet(efTOP,nfile,fnm))
+ top_update(opt2fn("-p",nfile,fnm),ins,rm_p,mtop);
+
+ sfree(pbc);
+ sfree(rest_at);
+ if (pieces>1) { sfree(piecename); }
+
+ membed->it_xy=it_xy;
+ membed->it_z=it_z;
+ membed->pos_ins=pos_ins;
+ membed->r_ins=r_ins;
+ }
+}
wall_atomtype[STRLEN],wall_density[STRLEN],deform[STRLEN],QMMM[STRLEN];
static char foreign_lambda[STRLEN];
static char **pull_grp;
+static char **rot_grp;
static char anneal[STRLEN],anneal_npoints[STRLEN],
anneal_time[STRLEN],anneal_temp[STRLEN];
static char QMmethod[STRLEN],QMbasis[STRLEN],QMcharge[STRLEN],QMmult[STRLEN],
snew(ir->pull,1);
pull_grp = read_pullparams(&ninp,&inp,ir->pull,&opts->pull_start,wi);
}
+
+ /* Enforced rotation */
+ CCTYPE("ENFORCED ROTATION");
+ CTYPE("Enforced rotation: No or Yes");
+ EETYPE("rotation", ir->bRot, yesno_names);
+ if (ir->bRot) {
+ snew(ir->rot,1);
+ rot_grp = read_rotparams(&ninp,&inp,ir->rot,wi);
+ }
/* Refinement */
CCTYPE("NMR refinement stuff");
if (ir->ePull != epullNO) {
make_pull_groups(ir->pull,pull_grp,grps,gnames);
}
+
+ if (ir->bRot) {
+ make_rotation_groups(ir->rot,rot_grp,grps,gnames);
+ }
nacc = str_nelem(acc,MAXPTR,ptr1);
nacg = str_nelem(accgrps,MAXPTR,ptr2);
* If bStart adds the distance to the initial reference location.
*/
+extern char **read_rotparams(int *ninp_p,t_inpfile **inp,t_rot *rot,warninp_t wi);
+/* Reads enforced rotation parameters, returns a list of the rot group names */
+
+extern void make_rotation_groups(t_rot *rot,char **rotgnames,
+ t_blocka *grps,char **gnames);
+/* Process the rotation parameters after reading the index groups */
+
+extern void set_reference_positions(t_rot *rot, gmx_mtop_t *mtop, rvec *x, matrix box,
+ const char *fn, gmx_bool bSet, warninp_t wi);
+
#endif /* _readir_h */
--- /dev/null
+/*
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "vec.h"
+#include "smalloc.h"
+#include "readir.h"
+#include "names.h"
+#include "futil.h"
+#include "trnio.h"
+#include "txtdump.h"
+
+static char *RotStr = {"Enforced rotation:"};
+
+
+static char s_vec[STRLEN];
+
+
+static void string2dvec(char buf[], dvec nums)
+{
+ if (sscanf(buf,"%lf%lf%lf",&nums[0],&nums[1],&nums[2]) != 3)
+ gmx_fatal(FARGS,"Expected three numbers at input line %s",buf);
+}
+
+
+extern char **read_rotparams(int *ninp_p,t_inpfile **inp_p,t_rot *rot,
+ warninp_t wi)
+{
+ int ninp,g,m;
+ t_inpfile *inp;
+ const char *tmp;
+ char **grpbuf;
+ char buf[STRLEN];
+ char warn_buf[STRLEN];
+ dvec vec;
+ t_rotgrp *rotg;
+
+ ninp = *ninp_p;
+ inp = *inp_p;
+
+ /* read rotation parameters */
+ CTYPE("Output frequency for angle, torque and rotation potential energy for the whole group");
+ ITYPE("rot_nstrout", rot->nstrout, 100);
+ CTYPE("Output frequency for per-slab data (angles, torques and slab centers)");
+ ITYPE("rot_nstsout", rot->nstsout, 1000);
+ CTYPE("Number of rotation groups");
+ ITYPE("rot_ngroups", rot->ngrp,1);
+
+ if (rot->ngrp < 1)
+ {
+ gmx_fatal(FARGS,"rot_ngroups should be >= 1");
+ }
+
+ snew(rot->grp,rot->ngrp);
+
+ /* Read the rotation groups */
+ snew(grpbuf,rot->ngrp);
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ snew(grpbuf[g],STRLEN);
+ CTYPE("Rotation group name");
+ sprintf(buf,"rot_group%d",g);
+ STYPE(buf, grpbuf[g], "");
+
+ CTYPE("Rotation potential. Can be iso, iso-pf, pm, pm-pf, rm, rm-pf, rm2, rm2-pf, flex, flex-t, flex2, flex2-t");
+ sprintf(buf,"rot_type%d",g);
+ ETYPE(buf, rotg->eType, erotg_names);
+
+ CTYPE("Use mass-weighting of the rotation group positions");
+ sprintf(buf,"rot_massw%d",g);
+ ETYPE(buf, rotg->bMassW, yesno_names);
+
+ CTYPE("Rotation vector, will get normalized");
+ sprintf(buf,"rot_vec%d",g);
+ STYPE(buf, s_vec, "1.0 0.0 0.0");
+ string2dvec(s_vec,vec);
+ /* Normalize the rotation vector */
+ if (dnorm(vec) != 0)
+ {
+ dsvmul(1.0/dnorm(vec),vec,vec);
+ }
+ else
+ {
+ sprintf(warn_buf, "rot_vec%d = 0", g);
+ warning_error(wi, warn_buf);
+ }
+ fprintf(stderr, "%s Group %d (%s) normalized rot. vector: %f %f %f\n",
+ RotStr, g, erotg_names[rotg->eType], vec[0], vec[1], vec[2]);
+ for(m=0; m<DIM; m++)
+ rotg->vec[m] = vec[m];
+
+ CTYPE("Pivot point for the potentials iso, pm, rm, and rm2 [nm]");
+ sprintf(buf,"rot_pivot%d",g);
+ STYPE(buf, s_vec, "0.0 0.0 0.0");
+ clear_dvec(vec);
+ if ( (rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2) )
+ string2dvec(s_vec,vec);
+ for(m=0; m<DIM; m++)
+ rotg->pivot[m] = vec[m];
+
+ CTYPE("Rotation rate [degree/ps] and force constant [kJ/(mol*nm^2)]");
+ sprintf(buf,"rot_rate%d",g);
+ RTYPE(buf, rotg->rate, 0.0);
+
+ sprintf(buf,"rot_k%d",g);
+ RTYPE(buf, rotg->k, 0.0);
+ if (rotg->k <= 0.0)
+ {
+ sprintf(warn_buf, "rot_k%d <= 0", g);
+ warning_note(wi, warn_buf);
+ }
+
+ CTYPE("Slab distance for flexible axis rotation [nm]");
+ sprintf(buf,"rot_slab_dist%d",g);
+ RTYPE(buf, rotg->slab_dist, 1.5);
+ if (rotg->slab_dist <= 0.0)
+ {
+ sprintf(warn_buf, "rot_slab_dist%d <= 0", g);
+ warning_error(wi, warn_buf);
+ }
+
+ CTYPE("Minimum value of Gaussian function for the force to be evaluated (for flex* potentials)");
+ sprintf(buf,"rot_min_gauss%d",g);
+ RTYPE(buf, rotg->min_gaussian, 1e-3);
+ if (rotg->min_gaussian <= 0.0)
+ {
+ sprintf(warn_buf, "rot_min_gauss%d <= 0", g);
+ warning_error(wi, warn_buf);
+ }
+
+ CTYPE("Value of additive constant epsilon' [nm^2] for rm2* and flex2* potentials");
+ sprintf(buf, "rot_eps%d",g);
+ RTYPE(buf, rotg->eps, 1e-4);
+ if ( (rotg->eps <= 0.0) && (rotg->eType==erotgRM2 || rotg->eType==erotgFLEX2) )
+ {
+ sprintf(warn_buf, "rot_eps%d <= 0", g);
+ warning_error(wi, warn_buf);
+ }
+
+ CTYPE("Fitting method to determine angle of rotation group (rmsd or norm) (flex* potentials)");
+ sprintf(buf,"rot_fit_method%d",g);
+ ETYPE(buf, rotg->eFittype, erotg_fitnames);
+ }
+
+ *ninp_p = ninp;
+ *inp_p = inp;
+
+ return grpbuf;
+}
+
+
+/* Check whether the box is unchanged */
+static void check_box(matrix f_box, matrix box, char fn[], warninp_t wi)
+{
+ int i,ii;
+ gmx_bool bSame=TRUE;
+ char warn_buf[STRLEN];
+
+
+ for (i=0; i<DIM; i++)
+ for (ii=0; ii<DIM; ii++)
+ if (f_box[i][ii] != box[i][ii])
+ bSame = FALSE;
+ if (!bSame)
+ {
+ sprintf(warn_buf, "%s Box size in reference file %s differs from actual box size!",
+ RotStr, fn);
+ warning(wi, warn_buf);
+ pr_rvecs(stderr,0,"Your box is:",box ,3);
+ pr_rvecs(stderr,0,"Box in file:",f_box,3);
+ }
+}
+
+
+/* Extract the reference positions for the rotation group(s) */
+extern void set_reference_positions(
+ t_rot *rot, gmx_mtop_t *mtop, rvec *x, matrix box,
+ const char *fn, gmx_bool bSet, warninp_t wi)
+{
+ int g,i,ii;
+ t_rotgrp *rotg;
+ t_trnheader header; /* Header information of reference file */
+ char base[STRLEN],extension[STRLEN],reffile[STRLEN];
+ char *extpos;
+ rvec f_box[3]; /* Box from reference file */
+
+
+ /* Base name and extension of the reference file: */
+ strncpy(base, fn, STRLEN - 1);
+ extpos = strrchr(base, '.');
+ strcpy(extension,extpos+1);
+ *extpos = '\0';
+
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ fprintf(stderr, "%s group %d has %d reference positions.\n",RotStr,g,rotg->nat);
+ snew(rotg->x_ref, rotg->nat);
+
+ /* Construct the name for the file containing the reference positions for this group: */
+ sprintf(reffile, "%s.%d.%s", base,g,extension);
+
+ /* If the base filename for the reference position files was explicitly set by
+ * the user, we issue a fatal error if the group file can not be found */
+ if (bSet && !gmx_fexist(reffile))
+ {
+ gmx_fatal(FARGS, "%s The file containing the reference positions was not found.\n"
+ "Expected the file '%s' for group %d.\n",
+ RotStr, reffile, g);
+ }
+
+ if (gmx_fexist(reffile))
+ {
+ fprintf(stderr, " Reading them from %s.\n", reffile);
+ read_trnheader(reffile, &header);
+ if (rotg->nat != header.natoms)
+ gmx_fatal(FARGS,"Number of atoms in file %s (%d) does not match the number of atoms in rotation group (%d)!\n",
+ reffile, header.natoms, rotg->nat);
+ read_trn(reffile, &header.step, &header.t, &header.lambda, f_box, &header.natoms, rotg->x_ref, NULL, NULL);
+
+ /* Check whether the box is unchanged and output a warning if not: */
+ check_box(f_box,box,reffile,wi);
+ }
+ else
+ {
+ fprintf(stderr, " Saving them to %s.\n", reffile);
+ for(i=0; i<rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+ copy_rvec(x[ii], rotg->x_ref[i]);
+ }
+ write_trn(reffile,g,0.0,0.0,box,rotg->nat,rotg->x_ref,NULL,NULL);
+ }
+ }
+}
+
+
+extern void make_rotation_groups(t_rot *rot,char **rotgnames,t_blocka *grps,char **gnames)
+{
+ int g,ig=-1,i;
+ t_rotgrp *rotg;
+
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ ig = search_string(rotgnames[g],grps->nr,gnames);
+ rotg->nat = grps->index[ig+1] - grps->index[ig];
+
+ if (rotg->nat > 0)
+ {
+ fprintf(stderr,"Rotation group %d '%s' has %d atoms\n",g,rotgnames[g],rotg->nat);
+ snew(rotg->ind,rotg->nat);
+ for(i=0; i<rotg->nat; i++)
+ rotg->ind[i] = grps->a[grps->index[ig]+i];
+ }
+ else
+ gmx_fatal(FARGS,"Rotation group %d '%s' is empty",g,rotgnames[g]);
+ }
+}
#include "mdrun.h"
#include "network.h"
#include "pull.h"
+#include "pull_rotation.h"
#include "names.h"
#include "disre.h"
#include "orires.h"
#include "sighandler.h"
#include "tpxio.h"
#include "txtdump.h"
+#include "membed.h"
#include "md_openmm.h"
gmx_edsam_t ed=NULL;
t_commrec *cr_old=cr;
int nthreads=1;
+ gmx_membed_t *membed=NULL;
/* CAUTION: threads may be started later on in this function, so
cr doesn't reflect the final parallel state right now */
}
/* END OF CAUTION: cr is now reliable */
+ /* g_membed initialisation *
+ * Because we change the mtop, init_membed is called before the init_parallel *
+ * (in case we ever want to make it run in parallel) */
+ if (opt2bSet("-membed",nfile,fnm))
+ {
+ fprintf(stderr,"Entering membed code");
+ snew(membed,1);
+ init_membed(fplog,membed,nfile,fnm,mtop,inputrec,state,cr,&cpt_period);
+ }
+
if (PAR(cr))
{
/* now broadcast everything to the non-master nodes/threads: */
init_pull(fplog,inputrec,nfile,fnm,mtop,cr,oenv,
EI_DYNAMICS(inputrec->eI) && MASTER(cr),Flags);
}
+
+ if (inputrec->bRot)
+ {
+ /* Initialize enforced rotation code */
+ init_rot(fplog,inputrec,nfile,fnm,cr,state->x,state->box,mtop,oenv,
+ bVerbose,Flags);
+ }
constr = init_constraints(fplog,mtop,inputrec,ed,state,cr);
fcd,state,
mdatoms,nrnb,wcycle,ed,fr,
repl_ex_nst,repl_ex_seed,
+ membed,
cpt_period,max_hours,
deviceOptions,
Flags,
{
finish_pull(fplog,inputrec->pull);
}
+
+ if (inputrec->bRot)
+ {
+ finish_rot(fplog,inputrec->rot);
+ }
+
}
else
{
finish_run(fplog,cr,ftp2fn(efSTO,nfile,fnm),
inputrec,nrnb,wcycle,&runtime,
EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
+
+ if (opt2bSet("-membed",nfile,fnm))
+ {
+ sfree(membed);
+ }
/* Does what it says */
print_date_and_time(fplog,cr->nodeid,"Finished mdrun",&runtime);
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
\ No newline at end of file
+++ /dev/null
-.deps
-.libs
+++ /dev/null
-
-file(GLOB MDLIB_SOURCES *.c)
-
-# Files called xxx_test.c are test drivers with a main() function for
-# module xxx.c, so they should not be included in the library
-file(GLOB_RECURSE NOT_MDLIB_SOURCES *_test.c *\#*)
-list(REMOVE_ITEM MDLIB_SOURCES ${NOT_MDLIB_SOURCES})
-
-add_library(md ${MDLIB_SOURCES})
-target_link_libraries(md gmx ${GMX_EXTRA_LIBRARIES} ${FFT_LIBRARIES} ${XML_LIBRARIES})
-set_target_properties(md PROPERTIES OUTPUT_NAME "md${GMX_LIBS_SUFFIX}" SOVERSION ${SOVERSION} INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
-
-install(TARGETS md DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)
-
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libmd.pc.cmakein ${CMAKE_CURRENT_BINARY_DIR}/libmd.pc @ONLY)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmd.pc
- DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
- RENAME "libmd${GMX_LIBS_SUFFIX}.pc"
- COMPONENT development)
+++ /dev/null
-## Process this file with automake to produce Makefile.in
-# Note: Makefile is automatically generated from Makefile.in by the configure
-# script, and Makefile.in is generated from Makefile.am by automake.
-
-AM_CPPFLAGS = -I$(top_srcdir)/include -DGMXLIBDIR=\"$(datadir)/top\"
-
-libmd@LIBSUFFIX@_la_LIBADD = ../gmxlib/libgmx@LIBSUFFIX@.la
-libmd@LIBSUFFIX@_la_DEPENDENCIES = ../gmxlib/libgmx@LIBSUFFIX@.la
-libmd@LIBSUFFIX@_la_LDFLAGS = -no-undefined -version-info @SHARED_VERSION_INFO@ $(FFT_LIBS) $(XML_LIBS) $(PTHREAD_LIBS)
-
-lib_LTLIBRARIES = libmd@LIBSUFFIX@.la
-
-pkgconfigdir = ${libdir}/pkgconfig
-pkgconfig_DATA = libmd@LIBSUFFIX@.pc
-
-EXTRA_DIST = libmd.pc.cmakein
-
-libmd@LIBSUFFIX@_la_SOURCES = \
- calcmu.c calcvir.c constr.c \
- coupling.c \
- domdec.c domdec_box.c domdec_con.c \
- domdec_network.c domdec_setup.c domdec_top.c \
- ebin.c \
- edsam.c ewald.c \
- force.c forcerec.c \
- ghat.c init.c \
- mdatom.c mdebin.c minimize.c \
- mvxvf.c ns.c nsgrid.c \
- perf_est.c genborn.c \
- genborn_sse2_single.c \
- genborn_sse2_single.h \
- genborn_sse2_double.c \
- genborn_sse2_double.h \
- genborn_allvsall.c \
- genborn_allvsall.h \
- genborn_allvsall_sse2_single.c \
- genborn_allvsall_sse2_single.h \
- genborn_allvsall_sse2_double.c \
- genborn_allvsall_sse2_double.h \
- gmx_qhop_parm.c gmx_qhop_parm.h \
- gmx_qhop_xml.c gmx_qhop_xml.h \
- groupcoord.c groupcoord.h \
- pme.c pme_pp.c pppm.c \
- partdec.c pull.c pullutil.c \
- rf_util.c shakef.c sim_util.c \
- shellfc.c stat.c \
- tables.c tgroup.c tpi.c \
- update.c vcm.c vsite.c \
- wall.c wnblist.c \
- csettle.c clincs.c \
- qmmm.c gmx_fft.c gmx_parallel_3dfft.c \
- fft5d.c fft5d.h \
- gmx_wallcycle.c \
- qm_gaussian.c qm_mopac.c qm_gamess.c \
- gmx_fft_fftw2.c gmx_fft_fftw3.c gmx_fft_fftpack.c \
- gmx_fft_mkl.c qm_orca.c mdebin_bar.c \
- mdebin_bar.h
-
-LDADD = ../mdlib/libmd@LIBSUFFIX@.la ../gmxlib/libgmx@LIBSUFFIX@.la
-
-EXTRA_PROGRAMS = gmx_qhop_db_test
-
-gmx_qhop_db_test_LDADD = ../mdlib/libmd@LIBSUFFIX@.la ../gmxlib/libgmx@LIBSUFFIX@.la ../kernel/libgmxpreprocess@LIBSUFFIX@.la
-
-# clean all libtool libraries, since the target names might have changed
-CLEANFILES = *.la *~ \\\#*
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This file is part of Gromacs Copyright (c) 1991-2008
- * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org
- *
- * And Hey:
- * Gnomes, ROck Monsters And Chili Sauce
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <time.h>
-#include <math.h>
-#include <string.h>
-#include <stdlib.h>
-#include "typedefs.h"
-#include "smalloc.h"
-#include "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 "names.h"
-#include "pdbio.h"
-#include "futil.h"
-#include "force.h"
-#include "pme.h"
-#include "pull.h"
-#include "gmx_wallcycle.h"
-#include "mdrun.h"
-#include "nsgrid.h"
-#include "shellfc.h"
-#include "mtop_util.h"
-#include "gmxfio.h"
-#include "gmx_ga2la.h"
-#include "gmx_sort.h"
-
-#ifdef GMX_LIB_MPI
-#include <mpi.h>
-#endif
-#ifdef GMX_THREADS
-#include "tmpi.h"
-#endif
-
-#define DDRANK(dd,rank) (rank)
-#define DDMASTERRANK(dd) (dd->masterrank)
-
-typedef struct gmx_domdec_master
-{
- /* The cell boundaries */
- real **cell_x;
- /* The global charge group division */
- int *ncg; /* Number of home charge groups for each node */
- int *index; /* Index of nnodes+1 into cg */
- int *cg; /* Global charge group index */
- int *nat; /* Number of home atoms for each node. */
- int *ibuf; /* Buffer for communication */
- rvec *vbuf; /* Buffer for state scattering and gathering */
-} gmx_domdec_master_t;
-
-typedef struct
-{
- /* The numbers of charge groups to send and receive for each cell
- * that requires communication, the last entry contains the total
- * number of atoms that needs to be communicated.
- */
- int nsend[DD_MAXIZONE+2];
- int nrecv[DD_MAXIZONE+2];
- /* The charge groups to send */
- int *index;
- int nalloc;
- /* The atom range for non-in-place communication */
- int cell2at0[DD_MAXIZONE];
- int cell2at1[DD_MAXIZONE];
-} gmx_domdec_ind_t;
-
-typedef struct
-{
- int np; /* Number of grid pulses in this dimension */
- int np_dlb; /* For dlb, for use with edlbAUTO */
- gmx_domdec_ind_t *ind; /* The indices to communicate, size np */
- int np_nalloc;
- gmx_bool bInPlace; /* Can we communicate in place? */
-} gmx_domdec_comm_dim_t;
-
-typedef struct
-{
- gmx_bool *bCellMin; /* Temp. var.: is this cell size at the limit */
- real *cell_f; /* State var.: cell boundaries, box relative */
- real *old_cell_f; /* Temp. var.: old cell size */
- real *cell_f_max0; /* State var.: max lower boundary, incl neighbors */
- real *cell_f_min1; /* State var.: min upper boundary, incl neighbors */
- real *bound_min; /* Temp. var.: lower limit for cell boundary */
- real *bound_max; /* Temp. var.: upper limit for cell boundary */
- gmx_bool bLimited; /* State var.: is DLB limited in this dim and row */
- real *buf_ncd; /* Temp. var. */
-} gmx_domdec_root_t;
-
-#define DD_NLOAD_MAX 9
-
-/* Here floats are accurate enough, since these variables
- * only influence the load balancing, not the actual MD results.
- */
-typedef struct
-{
- int nload;
- float *load;
- float sum;
- float max;
- float sum_m;
- float cvol_min;
- float mdf;
- float pme;
- int flags;
-} gmx_domdec_load_t;
-
-typedef struct
-{
- int nsc;
- int ind_gl;
- int ind;
-} gmx_cgsort_t;
-
-typedef struct
-{
- gmx_cgsort_t *sort1,*sort2;
- int sort_nalloc;
- gmx_cgsort_t *sort_new;
- int sort_new_nalloc;
- int *ibuf;
- int ibuf_nalloc;
-} gmx_domdec_sort_t;
-
-typedef struct
-{
- rvec *v;
- int nalloc;
-} vec_rvec_t;
-
-/* This enum determines the order of the coordinates.
- * ddnatHOME and ddnatZONE should be first and second,
- * the others can be ordered as wanted.
- */
-enum { ddnatHOME, ddnatZONE, ddnatVSITE, ddnatCON, ddnatNR };
-
-enum { edlbAUTO, edlbNO, edlbYES, edlbNR };
-const char *edlb_names[edlbNR] = { "auto", "no", "yes" };
-
-typedef struct
-{
- int dim; /* The dimension */
- gmx_bool dim_match;/* Tells if DD and PME dims match */
- int nslab; /* The number of PME slabs in this dimension */
- real *slb_dim_f; /* Cell sizes for determining the PME comm. with SLB */
- int *pp_min; /* The minimum pp node location, size nslab */
- int *pp_max; /* The maximum pp node location,size nslab */
- int maxshift; /* The maximum shift for coordinate redistribution in PME */
-} gmx_ddpme_t;
-
-typedef struct
-{
- real min0; /* The minimum bottom of this zone */
- real max1; /* The maximum top of this zone */
- real mch0; /* The maximum bottom communicaton height for this zone */
- real mch1; /* The maximum top communicaton height for this zone */
- real p1_0; /* The bottom value of the first cell in this zone */
- real p1_1; /* The top value of the first cell in this zone */
-} gmx_ddzone_t;
-
-typedef struct gmx_domdec_comm
-{
- /* All arrays are indexed with 0 to dd->ndim (not Cartesian indexing),
- * unless stated otherwise.
- */
-
- /* The number of decomposition dimensions for PME, 0: no PME */
- int npmedecompdim;
- /* The number of nodes doing PME (PP/PME or only PME) */
- int npmenodes;
- int npmenodes_x;
- int npmenodes_y;
- /* The communication setup including the PME only nodes */
- gmx_bool bCartesianPP_PME;
- ivec ntot;
- int cartpmedim;
- int *pmenodes; /* size npmenodes */
- int *ddindex2simnodeid; /* size npmenodes, only with bCartesianPP
- * but with bCartesianPP_PME */
- gmx_ddpme_t ddpme[2];
-
- /* The DD particle-particle nodes only */
- gmx_bool bCartesianPP;
- int *ddindex2ddnodeid; /* size npmenode, only with bCartesianPP_PME */
-
- /* The global charge groups */
- t_block cgs_gl;
-
- /* Should we sort the cgs */
- int nstSortCG;
- gmx_domdec_sort_t *sort;
-
- /* Are there bonded and multi-body interactions between charge groups? */
- gmx_bool bInterCGBondeds;
- gmx_bool bInterCGMultiBody;
-
- /* Data for the optional bonded interaction atom communication range */
- gmx_bool bBondComm;
- t_blocka *cglink;
- char *bLocalCG;
-
- /* The DLB option */
- int eDLB;
- /* Are we actually using DLB? */
- gmx_bool bDynLoadBal;
-
- /* Cell sizes for static load balancing, first index cartesian */
- real **slb_frac;
-
- /* The width of the communicated boundaries */
- real cutoff_mbody;
- real cutoff;
- /* The minimum cell size (including triclinic correction) */
- rvec cellsize_min;
- /* For dlb, for use with edlbAUTO */
- rvec cellsize_min_dlb;
- /* The lower limit for the DD cell size with DLB */
- real cellsize_limit;
- /* Effectively no NB cut-off limit with DLB for systems without PBC? */
- gmx_bool bVacDLBNoLimit;
-
- /* tric_dir is only stored here because dd_get_ns_ranges needs it */
- ivec tric_dir;
- /* box0 and box_size are required with dim's without pbc and -gcom */
- rvec box0;
- rvec box_size;
-
- /* The cell boundaries */
- rvec cell_x0;
- rvec cell_x1;
-
- /* The old location of the cell boundaries, to check cg displacements */
- rvec old_cell_x0;
- rvec old_cell_x1;
-
- /* The communication setup and charge group boundaries for the zones */
- gmx_domdec_zones_t zones;
-
- /* The zone limits for DD dimensions 1 and 2 (not 0), determined from
- * cell boundaries of neighboring cells for dynamic load balancing.
- */
- gmx_ddzone_t zone_d1[2];
- gmx_ddzone_t zone_d2[2][2];
-
- /* The coordinate/force communication setup and indices */
- gmx_domdec_comm_dim_t cd[DIM];
- /* The maximum number of cells to communicate with in one dimension */
- int maxpulse;
-
- /* Which cg distribution is stored on the master node */
- int master_cg_ddp_count;
-
- /* The number of cg's received from the direct neighbors */
- int zone_ncg1[DD_MAXZONE];
-
- /* The atom counts, the range for each type t is nat[t-1] <= at < nat[t] */
- int nat[ddnatNR];
-
- /* Communication buffer for general use */
- int *buf_int;
- int nalloc_int;
-
- /* Communication buffer for general use */
- vec_rvec_t vbuf;
-
- /* Communication buffers only used with multiple grid pulses */
- int *buf_int2;
- int nalloc_int2;
- vec_rvec_t vbuf2;
-
- /* Communication buffers for local redistribution */
- int **cggl_flag;
- int cggl_flag_nalloc[DIM*2];
- rvec **cgcm_state;
- int cgcm_state_nalloc[DIM*2];
-
- /* Cell sizes for dynamic load balancing */
- gmx_domdec_root_t **root;
- real *cell_f_row;
- real cell_f0[DIM];
- real cell_f1[DIM];
- real cell_f_max0[DIM];
- real cell_f_min1[DIM];
-
- /* Stuff for load communication */
- gmx_bool bRecordLoad;
- gmx_domdec_load_t *load;
-#ifdef GMX_MPI
- MPI_Comm *mpi_comm_load;
-#endif
- /* Cycle counters */
- float cycl[ddCyclNr];
- int cycl_n[ddCyclNr];
- float cycl_max[ddCyclNr];
- /* Flop counter (0=no,1=yes,2=with (eFlop-1)*5% noise */
- int eFlop;
- double flop;
- int flop_n;
- /* Have often have did we have load measurements */
- int n_load_have;
- /* Have often have we collected the load measurements */
- int n_load_collect;
-
- /* Statistics */
- double sum_nat[ddnatNR-ddnatZONE];
- int ndecomp;
- int nload;
- double load_step;
- double load_sum;
- double load_max;
- ivec load_lim;
- double load_mdf;
- double load_pme;
-
- /* The last partition step */
- gmx_large_int_t partition_step;
-
- /* Debugging */
- int nstDDDump;
- int nstDDDumpGrid;
- int DD_debug;
-} gmx_domdec_comm_t;
-
-/* The size per charge group of the cggl_flag buffer in gmx_domdec_comm_t */
-#define DD_CGIBS 2
-
-/* The flags for the cggl_flag buffer in gmx_domdec_comm_t */
-#define DD_FLAG_NRCG 65535
-#define DD_FLAG_FW(d) (1<<(16+(d)*2))
-#define DD_FLAG_BW(d) (1<<(16+(d)*2+1))
-
-/* Zone permutation required to obtain consecutive charge groups
- * for neighbor searching.
- */
-static const int zone_perm[3][4] = { {0,0,0,0},{1,0,0,0},{3,0,1,2} };
-
-/* dd_zo and dd_zp3/dd_zp2 are set up such that i zones with non-zero
- * components see only j zones with that component 0.
- */
-
-/* The DD zone order */
-static const ivec dd_zo[DD_MAXZONE] =
- {{0,0,0},{1,0,0},{1,1,0},{0,1,0},{0,1,1},{0,0,1},{1,0,1},{1,1,1}};
-
-/* The 3D setup */
-#define dd_z3n 8
-#define dd_zp3n 4
-static const ivec dd_zp3[dd_zp3n] = {{0,0,8},{1,3,6},{2,5,6},{3,5,7}};
-
-/* The 2D setup */
-#define dd_z2n 4
-#define dd_zp2n 2
-static const ivec dd_zp2[dd_zp2n] = {{0,0,4},{1,3,4}};
-
-/* The 1D setup */
-#define dd_z1n 2
-#define dd_zp1n 1
-static const ivec dd_zp1[dd_zp1n] = {{0,0,2}};
-
-/* Factors used to avoid problems due to rounding issues */
-#define DD_CELL_MARGIN 1.0001
-#define DD_CELL_MARGIN2 1.00005
-/* Factor to account for pressure scaling during nstlist steps */
-#define DD_PRES_SCALE_MARGIN 1.02
-
-/* Allowed performance loss before we DLB or warn */
-#define DD_PERF_LOSS 0.05
-
-#define DD_CELL_F_SIZE(dd,di) ((dd)->nc[(dd)->dim[(di)]]+1+(di)*2+1+(di))
-
-/* Use separate MPI send and receive commands
- * when nnodes <= GMX_DD_NNODES_SENDRECV.
- * This saves memory (and some copying for small nnodes).
- * For high parallelization scatter and gather calls are used.
- */
-#define GMX_DD_NNODES_SENDRECV 4
-
-
-/*
-#define dd_index(n,i) ((((i)[ZZ]*(n)[YY] + (i)[YY])*(n)[XX]) + (i)[XX])
-
-static void index2xyz(ivec nc,int ind,ivec xyz)
-{
- xyz[XX] = ind % nc[XX];
- xyz[YY] = (ind / nc[XX]) % nc[YY];
- xyz[ZZ] = ind / (nc[YY]*nc[XX]);
-}
-*/
-
-/* This order is required to minimize the coordinate communication in PME
- * which uses decomposition in the x direction.
- */
-#define dd_index(n,i) ((((i)[XX]*(n)[YY] + (i)[YY])*(n)[ZZ]) + (i)[ZZ])
-
-static void ddindex2xyz(ivec nc,int ind,ivec xyz)
-{
- xyz[XX] = ind / (nc[YY]*nc[ZZ]);
- xyz[YY] = (ind / nc[ZZ]) % nc[YY];
- xyz[ZZ] = ind % nc[ZZ];
-}
-
-static int ddcoord2ddnodeid(gmx_domdec_t *dd,ivec c)
-{
- int ddindex;
- int ddnodeid=-1;
-
- ddindex = dd_index(dd->nc,c);
- if (dd->comm->bCartesianPP_PME)
- {
- ddnodeid = dd->comm->ddindex2ddnodeid[ddindex];
- }
- else if (dd->comm->bCartesianPP)
- {
-#ifdef GMX_MPI
- MPI_Cart_rank(dd->mpi_comm_all,c,&ddnodeid);
-#endif
- }
- else
- {
- ddnodeid = ddindex;
- }
-
- return ddnodeid;
-}
-
-static gmx_bool dynamic_dd_box(gmx_ddbox_t *ddbox,t_inputrec *ir)
-{
- return (ddbox->nboundeddim < DIM || DYNAMIC_BOX(*ir));
-}
-
-int ddglatnr(gmx_domdec_t *dd,int i)
-{
- int atnr;
-
- if (dd == NULL)
- {
- atnr = i + 1;
- }
- else
- {
- if (i >= dd->comm->nat[ddnatNR-1])
- {
- gmx_fatal(FARGS,"glatnr called with %d, which is larger than the local number of atoms (%d)",i,dd->comm->nat[ddnatNR-1]);
- }
- atnr = dd->gatindex[i] + 1;
- }
-
- return atnr;
-}
-
-t_block *dd_charge_groups_global(gmx_domdec_t *dd)
-{
- return &dd->comm->cgs_gl;
-}
-
-static void vec_rvec_init(vec_rvec_t *v)
-{
- v->nalloc = 0;
- v->v = NULL;
-}
-
-static void vec_rvec_check_alloc(vec_rvec_t *v,int n)
-{
- if (n > v->nalloc)
- {
- v->nalloc = over_alloc_dd(n);
- srenew(v->v,v->nalloc);
- }
-}
-
-void dd_store_state(gmx_domdec_t *dd,t_state *state)
-{
- int i;
-
- if (state->ddp_count != dd->ddp_count)
- {
- gmx_incons("The state does not the domain decomposition state");
- }
-
- state->ncg_gl = dd->ncg_home;
- if (state->ncg_gl > state->cg_gl_nalloc)
- {
- state->cg_gl_nalloc = over_alloc_dd(state->ncg_gl);
- srenew(state->cg_gl,state->cg_gl_nalloc);
- }
- for(i=0; i<state->ncg_gl; i++)
- {
- state->cg_gl[i] = dd->index_gl[i];
- }
-
- state->ddp_count_cg_gl = dd->ddp_count;
-}
-
-gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd)
-{
- return &dd->comm->zones;
-}
-
-void dd_get_ns_ranges(gmx_domdec_t *dd,int icg,
- int *jcg0,int *jcg1,ivec shift0,ivec shift1)
-{
- gmx_domdec_zones_t *zones;
- int izone,d,dim;
-
- zones = &dd->comm->zones;
-
- izone = 0;
- while (icg >= zones->izone[izone].cg1)
- {
- izone++;
- }
-
- if (izone == 0)
- {
- *jcg0 = icg;
- }
- else if (izone < zones->nizone)
- {
- *jcg0 = zones->izone[izone].jcg0;
- }
- else
- {
- gmx_fatal(FARGS,"DD icg %d out of range: izone (%d) >= nizone (%d)",
- icg,izone,zones->nizone);
- }
-
- *jcg1 = zones->izone[izone].jcg1;
-
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- shift0[dim] = zones->izone[izone].shift0[dim];
- shift1[dim] = zones->izone[izone].shift1[dim];
- if (dd->comm->tric_dir[dim] || (dd->bGridJump && d > 0))
- {
- /* A conservative approach, this can be optimized */
- shift0[dim] -= 1;
- shift1[dim] += 1;
- }
- }
-}
-
-int dd_natoms_vsite(gmx_domdec_t *dd)
-{
- return dd->comm->nat[ddnatVSITE];
-}
-
-void dd_get_constraint_range(gmx_domdec_t *dd,int *at_start,int *at_end)
-{
- *at_start = dd->comm->nat[ddnatCON-1];
- *at_end = dd->comm->nat[ddnatCON];
-}
-
-void dd_move_x(gmx_domdec_t *dd,matrix box,rvec x[])
-{
- int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
- int *index,*cgindex;
- gmx_domdec_comm_t *comm;
- gmx_domdec_comm_dim_t *cd;
- gmx_domdec_ind_t *ind;
- rvec shift={0,0,0},*buf,*rbuf;
- gmx_bool bPBC,bScrew;
-
- comm = dd->comm;
-
- cgindex = dd->cgindex;
-
- buf = comm->vbuf.v;
-
- nzone = 1;
- nat_tot = dd->nat_home;
- for(d=0; d<dd->ndim; d++)
- {
- bPBC = (dd->ci[dd->dim[d]] == 0);
- bScrew = (bPBC && dd->bScrewPBC && dd->dim[d] == XX);
- if (bPBC)
- {
- copy_rvec(box[dd->dim[d]],shift);
- }
- cd = &comm->cd[d];
- for(p=0; p<cd->np; p++)
- {
- ind = &cd->ind[p];
- index = ind->index;
- n = 0;
- if (!bPBC)
- {
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- copy_rvec(x[j],buf[n]);
- n++;
- }
- }
- }
- else if (!bScrew)
- {
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- /* We need to shift the coordinates */
- rvec_add(x[j],shift,buf[n]);
- n++;
- }
- }
- }
- else
- {
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- /* Shift x */
- buf[n][XX] = x[j][XX] + shift[XX];
- /* Rotate y and z.
- * This operation requires a special shift force
- * treatment, which is performed in calc_vir.
- */
- buf[n][YY] = box[YY][YY] - x[j][YY];
- buf[n][ZZ] = box[ZZ][ZZ] - x[j][ZZ];
- n++;
- }
- }
- }
-
- if (cd->bInPlace)
- {
- rbuf = x + nat_tot;
- }
- else
- {
- rbuf = comm->vbuf2.v;
- }
- /* Send and receive the coordinates */
- dd_sendrecv_rvec(dd, d, dddirBackward,
- buf, ind->nsend[nzone+1],
- rbuf, ind->nrecv[nzone+1]);
- if (!cd->bInPlace)
- {
- j = 0;
- for(zone=0; zone<nzone; zone++)
- {
- for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
- {
- copy_rvec(rbuf[j],x[i]);
- j++;
- }
- }
- }
- nat_tot += ind->nrecv[nzone+1];
- }
- nzone += nzone;
- }
-}
-
-void dd_move_f(gmx_domdec_t *dd,rvec f[],rvec *fshift)
-{
- int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
- int *index,*cgindex;
- gmx_domdec_comm_t *comm;
- gmx_domdec_comm_dim_t *cd;
- gmx_domdec_ind_t *ind;
- rvec *buf,*sbuf;
- ivec vis;
- int is;
- gmx_bool bPBC,bScrew;
-
- comm = dd->comm;
-
- cgindex = dd->cgindex;
-
- buf = comm->vbuf.v;
-
- n = 0;
- nzone = comm->zones.n/2;
- nat_tot = dd->nat_tot;
- for(d=dd->ndim-1; d>=0; d--)
- {
- bPBC = (dd->ci[dd->dim[d]] == 0);
- bScrew = (bPBC && dd->bScrewPBC && dd->dim[d] == XX);
- if (fshift == NULL && !bScrew)
- {
- bPBC = FALSE;
- }
- /* Determine which shift vector we need */
- clear_ivec(vis);
- vis[dd->dim[d]] = 1;
- is = IVEC2IS(vis);
-
- cd = &comm->cd[d];
- for(p=cd->np-1; p>=0; p--) {
- ind = &cd->ind[p];
- nat_tot -= ind->nrecv[nzone+1];
- if (cd->bInPlace)
- {
- sbuf = f + nat_tot;
- }
- else
- {
- sbuf = comm->vbuf2.v;
- j = 0;
- for(zone=0; zone<nzone; zone++)
- {
- for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
- {
- copy_rvec(f[i],sbuf[j]);
- j++;
- }
- }
- }
- /* Communicate the forces */
- dd_sendrecv_rvec(dd, d, dddirForward,
- sbuf, ind->nrecv[nzone+1],
- buf, ind->nsend[nzone+1]);
- index = ind->index;
- /* Add the received forces */
- n = 0;
- if (!bPBC)
- {
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- rvec_inc(f[j],buf[n]);
- n++;
- }
- }
- }
- else if (!bScrew)
- {
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- rvec_inc(f[j],buf[n]);
- /* Add this force to the shift force */
- rvec_inc(fshift[is],buf[n]);
- n++;
- }
- }
- }
- else
- {
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- /* Rotate the force */
- f[j][XX] += buf[n][XX];
- f[j][YY] -= buf[n][YY];
- f[j][ZZ] -= buf[n][ZZ];
- if (fshift)
- {
- /* Add this force to the shift force */
- rvec_inc(fshift[is],buf[n]);
- }
- n++;
- }
- }
- }
- }
- nzone /= 2;
- }
-}
-
-void dd_atom_spread_real(gmx_domdec_t *dd,real v[])
-{
- int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
- int *index,*cgindex;
- gmx_domdec_comm_t *comm;
- gmx_domdec_comm_dim_t *cd;
- gmx_domdec_ind_t *ind;
- real *buf,*rbuf;
-
- comm = dd->comm;
-
- cgindex = dd->cgindex;
-
- buf = &comm->vbuf.v[0][0];
-
- nzone = 1;
- nat_tot = dd->nat_home;
- for(d=0; d<dd->ndim; d++)
- {
- cd = &comm->cd[d];
- for(p=0; p<cd->np; p++)
- {
- ind = &cd->ind[p];
- index = ind->index;
- n = 0;
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- buf[n] = v[j];
- n++;
- }
- }
-
- if (cd->bInPlace)
- {
- rbuf = v + nat_tot;
- }
- else
- {
- rbuf = &comm->vbuf2.v[0][0];
- }
- /* Send and receive the coordinates */
- dd_sendrecv_real(dd, d, dddirBackward,
- buf, ind->nsend[nzone+1],
- rbuf, ind->nrecv[nzone+1]);
- if (!cd->bInPlace)
- {
- j = 0;
- for(zone=0; zone<nzone; zone++)
- {
- for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
- {
- v[i] = rbuf[j];
- j++;
- }
- }
- }
- nat_tot += ind->nrecv[nzone+1];
- }
- nzone += nzone;
- }
-}
-
-void dd_atom_sum_real(gmx_domdec_t *dd,real v[])
-{
- int nzone,nat_tot,n,d,p,i,j,at0,at1,zone;
- int *index,*cgindex;
- gmx_domdec_comm_t *comm;
- gmx_domdec_comm_dim_t *cd;
- gmx_domdec_ind_t *ind;
- real *buf,*sbuf;
-
- comm = dd->comm;
-
- cgindex = dd->cgindex;
-
- buf = &comm->vbuf.v[0][0];
-
- n = 0;
- nzone = comm->zones.n/2;
- nat_tot = dd->nat_tot;
- for(d=dd->ndim-1; d>=0; d--)
- {
- cd = &comm->cd[d];
- for(p=cd->np-1; p>=0; p--) {
- ind = &cd->ind[p];
- nat_tot -= ind->nrecv[nzone+1];
- if (cd->bInPlace)
- {
- sbuf = v + nat_tot;
- }
- else
- {
- sbuf = &comm->vbuf2.v[0][0];
- j = 0;
- for(zone=0; zone<nzone; zone++)
- {
- for(i=ind->cell2at0[zone]; i<ind->cell2at1[zone]; i++)
- {
- sbuf[j] = v[i];
- j++;
- }
- }
- }
- /* Communicate the forces */
- dd_sendrecv_real(dd, d, dddirForward,
- sbuf, ind->nrecv[nzone+1],
- buf, ind->nsend[nzone+1]);
- index = ind->index;
- /* Add the received forces */
- n = 0;
- for(i=0; i<ind->nsend[nzone]; i++)
- {
- at0 = cgindex[index[i]];
- at1 = cgindex[index[i]+1];
- for(j=at0; j<at1; j++)
- {
- v[j] += buf[n];
- n++;
- }
- }
- }
- nzone /= 2;
- }
-}
-
-static void print_ddzone(FILE *fp,int d,int i,int j,gmx_ddzone_t *zone)
-{
- fprintf(fp,"zone d0 %d d1 %d d2 %d min0 %6.3f max1 %6.3f mch0 %6.3f mch1 %6.3f p1_0 %6.3f p1_1 %6.3f\n",
- d,i,j,
- zone->min0,zone->max1,
- zone->mch0,zone->mch0,
- zone->p1_0,zone->p1_1);
-}
-
-static void dd_sendrecv_ddzone(const gmx_domdec_t *dd,
- int ddimind,int direction,
- gmx_ddzone_t *buf_s,int n_s,
- gmx_ddzone_t *buf_r,int n_r)
-{
- rvec vbuf_s[5*2],vbuf_r[5*2];
- int i;
-
- for(i=0; i<n_s; i++)
- {
- vbuf_s[i*2 ][0] = buf_s[i].min0;
- vbuf_s[i*2 ][1] = buf_s[i].max1;
- vbuf_s[i*2 ][2] = buf_s[i].mch0;
- vbuf_s[i*2+1][0] = buf_s[i].mch1;
- vbuf_s[i*2+1][1] = buf_s[i].p1_0;
- vbuf_s[i*2+1][2] = buf_s[i].p1_1;
- }
-
- dd_sendrecv_rvec(dd, ddimind, direction,
- vbuf_s, n_s*2,
- vbuf_r, n_r*2);
-
- for(i=0; i<n_r; i++)
- {
- buf_r[i].min0 = vbuf_r[i*2 ][0];
- buf_r[i].max1 = vbuf_r[i*2 ][1];
- buf_r[i].mch0 = vbuf_r[i*2 ][2];
- buf_r[i].mch1 = vbuf_r[i*2+1][0];
- buf_r[i].p1_0 = vbuf_r[i*2+1][1];
- buf_r[i].p1_1 = vbuf_r[i*2+1][2];
- }
-}
-
-static void dd_move_cellx(gmx_domdec_t *dd,gmx_ddbox_t *ddbox,
- rvec cell_ns_x0,rvec cell_ns_x1)
-{
- int d,d1,dim,dim1,pos,buf_size,i,j,k,p,npulse,npulse_min;
- gmx_ddzone_t *zp,buf_s[5],buf_r[5],buf_e[5];
- rvec extr_s[2],extr_r[2];
- rvec dh;
- real dist_d,c=0,det;
- gmx_domdec_comm_t *comm;
- gmx_bool bPBC,bUse;
-
- comm = dd->comm;
-
- for(d=1; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- zp = (d == 1) ? &comm->zone_d1[0] : &comm->zone_d2[0][0];
- zp->min0 = cell_ns_x0[dim];
- zp->max1 = cell_ns_x1[dim];
- zp->mch0 = cell_ns_x0[dim];
- zp->mch1 = cell_ns_x1[dim];
- zp->p1_0 = cell_ns_x0[dim];
- zp->p1_1 = cell_ns_x1[dim];
- }
-
- for(d=dd->ndim-2; d>=0; d--)
- {
- dim = dd->dim[d];
- bPBC = (dim < ddbox->npbcdim);
-
- /* Use an rvec to store two reals */
- extr_s[d][0] = comm->cell_f0[d+1];
- extr_s[d][1] = comm->cell_f1[d+1];
- extr_s[d][2] = 0;
-
- pos = 0;
- /* Store the extremes in the backward sending buffer,
- * so the get updated separately from the forward communication.
- */
- for(d1=d; d1<dd->ndim-1; d1++)
- {
- /* We invert the order to be able to use the same loop for buf_e */
- buf_s[pos].min0 = extr_s[d1][1];
- buf_s[pos].max1 = extr_s[d1][0];
- buf_s[pos].mch0 = 0;
- buf_s[pos].mch1 = 0;
- /* Store the cell corner of the dimension we communicate along */
- buf_s[pos].p1_0 = comm->cell_x0[dim];
- buf_s[pos].p1_1 = 0;
- pos++;
- }
-
- buf_s[pos] = (dd->ndim == 2) ? comm->zone_d1[0] : comm->zone_d2[0][0];
- pos++;
-
- if (dd->ndim == 3 && d == 0)
- {
- buf_s[pos] = comm->zone_d2[0][1];
- pos++;
- buf_s[pos] = comm->zone_d1[0];
- pos++;
- }
-
- /* We only need to communicate the extremes
- * in the forward direction
- */
- npulse = comm->cd[d].np;
- if (bPBC)
- {
- /* Take the minimum to avoid double communication */
- npulse_min = min(npulse,dd->nc[dim]-1-npulse);
- }
- else
- {
- /* Without PBC we should really not communicate over
- * the boundaries, but implementing that complicates
- * the communication setup and therefore we simply
- * do all communication, but ignore some data.
- */
- npulse_min = npulse;
- }
- for(p=0; p<npulse_min; p++)
- {
- /* Communicate the extremes forward */
- bUse = (bPBC || dd->ci[dim] > 0);
-
- dd_sendrecv_rvec(dd, d, dddirForward,
- extr_s+d, dd->ndim-d-1,
- extr_r+d, dd->ndim-d-1);
-
- if (bUse)
- {
- for(d1=d; d1<dd->ndim-1; d1++)
- {
- extr_s[d1][0] = max(extr_s[d1][0],extr_r[d1][0]);
- extr_s[d1][1] = min(extr_s[d1][1],extr_r[d1][1]);
- }
- }
- }
-
- buf_size = pos;
- for(p=0; p<npulse; p++)
- {
- /* Communicate all the zone information backward */
- bUse = (bPBC || dd->ci[dim] < dd->nc[dim] - 1);
-
- dd_sendrecv_ddzone(dd, d, dddirBackward,
- buf_s, buf_size,
- buf_r, buf_size);
-
- clear_rvec(dh);
- if (p > 0)
- {
- for(d1=d+1; d1<dd->ndim; d1++)
- {
- /* Determine the decrease of maximum required
- * communication height along d1 due to the distance along d,
- * this avoids a lot of useless atom communication.
- */
- dist_d = comm->cell_x1[dim] - buf_r[0].p1_0;
-
- if (ddbox->tric_dir[dim])
- {
- /* c is the off-diagonal coupling between the cell planes
- * along directions d and d1.
- */
- c = ddbox->v[dim][dd->dim[d1]][dim];
- }
- else
- {
- c = 0;
- }
- det = (1 + c*c)*comm->cutoff*comm->cutoff - dist_d*dist_d;
- if (det > 0)
- {
- dh[d1] = comm->cutoff - (c*dist_d + sqrt(det))/(1 + c*c);
- }
- else
- {
- /* A negative value signals out of range */
- dh[d1] = -1;
- }
- }
- }
-
- /* Accumulate the extremes over all pulses */
- for(i=0; i<buf_size; i++)
- {
- if (p == 0)
- {
- buf_e[i] = buf_r[i];
- }
- else
- {
- if (bUse)
- {
- buf_e[i].min0 = min(buf_e[i].min0,buf_r[i].min0);
- buf_e[i].max1 = max(buf_e[i].max1,buf_r[i].max1);
- }
-
- if (dd->ndim == 3 && d == 0 && i == buf_size - 1)
- {
- d1 = 1;
- }
- else
- {
- d1 = d + 1;
- }
- if (bUse && dh[d1] >= 0)
- {
- buf_e[i].mch0 = max(buf_e[i].mch0,buf_r[i].mch0-dh[d1]);
- buf_e[i].mch1 = max(buf_e[i].mch1,buf_r[i].mch1-dh[d1]);
- }
- }
- /* Copy the received buffer to the send buffer,
- * to pass the data through with the next pulse.
- */
- buf_s[i] = buf_r[i];
- }
- if (((bPBC || dd->ci[dim]+npulse < dd->nc[dim]) && p == npulse-1) ||
- (!bPBC && dd->ci[dim]+1+p == dd->nc[dim]-1))
- {
- /* Store the extremes */
- pos = 0;
-
- for(d1=d; d1<dd->ndim-1; d1++)
- {
- extr_s[d1][1] = min(extr_s[d1][1],buf_e[pos].min0);
- extr_s[d1][0] = max(extr_s[d1][0],buf_e[pos].max1);
- pos++;
- }
-
- if (d == 1 || (d == 0 && dd->ndim == 3))
- {
- for(i=d; i<2; i++)
- {
- comm->zone_d2[1-d][i] = buf_e[pos];
- pos++;
- }
- }
- if (d == 0)
- {
- comm->zone_d1[1] = buf_e[pos];
- pos++;
- }
- }
- }
- }
-
- if (dd->ndim >= 2)
- {
- dim = dd->dim[1];
- for(i=0; i<2; i++)
- {
- if (debug)
- {
- print_ddzone(debug,1,i,0,&comm->zone_d1[i]);
- }
- cell_ns_x0[dim] = min(cell_ns_x0[dim],comm->zone_d1[i].min0);
- cell_ns_x1[dim] = max(cell_ns_x1[dim],comm->zone_d1[i].max1);
- }
- }
- if (dd->ndim >= 3)
- {
- dim = dd->dim[2];
- for(i=0; i<2; i++)
- {
- for(j=0; j<2; j++)
- {
- if (debug)
- {
- print_ddzone(debug,2,i,j,&comm->zone_d2[i][j]);
- }
- cell_ns_x0[dim] = min(cell_ns_x0[dim],comm->zone_d2[i][j].min0);
- cell_ns_x1[dim] = max(cell_ns_x1[dim],comm->zone_d2[i][j].max1);
- }
- }
- }
- for(d=1; d<dd->ndim; d++)
- {
- comm->cell_f_max0[d] = extr_s[d-1][0];
- comm->cell_f_min1[d] = extr_s[d-1][1];
- if (debug)
- {
- fprintf(debug,"Cell fraction d %d, max0 %f, min1 %f\n",
- d,comm->cell_f_max0[d],comm->cell_f_min1[d]);
- }
- }
-}
-
-static void dd_collect_cg(gmx_domdec_t *dd,
- t_state *state_local)
-{
- gmx_domdec_master_t *ma=NULL;
- int buf2[2],*ibuf,i,ncg_home=0,*cg=NULL,nat_home=0;
- t_block *cgs_gl;
-
- if (state_local->ddp_count == dd->comm->master_cg_ddp_count)
- {
- /* The master has the correct distribution */
- return;
- }
-
- if (state_local->ddp_count == dd->ddp_count)
- {
- ncg_home = dd->ncg_home;
- cg = dd->index_gl;
- nat_home = dd->nat_home;
- }
- else if (state_local->ddp_count_cg_gl == state_local->ddp_count)
- {
- cgs_gl = &dd->comm->cgs_gl;
-
- ncg_home = state_local->ncg_gl;
- cg = state_local->cg_gl;
- nat_home = 0;
- for(i=0; i<ncg_home; i++)
- {
- nat_home += cgs_gl->index[cg[i]+1] - cgs_gl->index[cg[i]];
- }
- }
- else
- {
- gmx_incons("Attempted to collect a vector for a state for which the charge group distribution is unknown");
- }
-
- buf2[0] = dd->ncg_home;
- buf2[1] = dd->nat_home;
- if (DDMASTER(dd))
- {
- ma = dd->ma;
- ibuf = ma->ibuf;
- }
- else
- {
- ibuf = NULL;
- }
- /* Collect the charge group and atom counts on the master */
- dd_gather(dd,2*sizeof(int),buf2,ibuf);
-
- if (DDMASTER(dd))
- {
- ma->index[0] = 0;
- for(i=0; i<dd->nnodes; i++)
- {
- ma->ncg[i] = ma->ibuf[2*i];
- ma->nat[i] = ma->ibuf[2*i+1];
- ma->index[i+1] = ma->index[i] + ma->ncg[i];
-
- }
- /* Make byte counts and indices */
- for(i=0; i<dd->nnodes; i++)
- {
- ma->ibuf[i] = ma->ncg[i]*sizeof(int);
- ma->ibuf[dd->nnodes+i] = ma->index[i]*sizeof(int);
- }
- if (debug)
- {
- fprintf(debug,"Initial charge group distribution: ");
- for(i=0; i<dd->nnodes; i++)
- fprintf(debug," %d",ma->ncg[i]);
- fprintf(debug,"\n");
- }
- }
-
- /* Collect the charge group indices on the master */
- dd_gatherv(dd,
- dd->ncg_home*sizeof(int),dd->index_gl,
- DDMASTER(dd) ? ma->ibuf : NULL,
- DDMASTER(dd) ? ma->ibuf+dd->nnodes : NULL,
- DDMASTER(dd) ? ma->cg : NULL);
-
- dd->comm->master_cg_ddp_count = state_local->ddp_count;
-}
-
-static void dd_collect_vec_sendrecv(gmx_domdec_t *dd,
- rvec *lv,rvec *v)
-{
- gmx_domdec_master_t *ma;
- int n,i,c,a,nalloc=0;
- rvec *buf=NULL;
- t_block *cgs_gl;
-
- ma = dd->ma;
-
- if (!DDMASTER(dd))
- {
-#ifdef GMX_MPI
- MPI_Send(lv,dd->nat_home*sizeof(rvec),MPI_BYTE,DDMASTERRANK(dd),
- dd->rank,dd->mpi_comm_all);
-#endif
- } else {
- /* Copy the master coordinates to the global array */
- cgs_gl = &dd->comm->cgs_gl;
-
- n = DDMASTERRANK(dd);
- a = 0;
- for(i=ma->index[n]; i<ma->index[n+1]; i++)
- {
- for(c=cgs_gl->index[ma->cg[i]]; c<cgs_gl->index[ma->cg[i]+1]; c++)
- {
- copy_rvec(lv[a++],v[c]);
- }
- }
-
- for(n=0; n<dd->nnodes; n++)
- {
- if (n != dd->rank)
- {
- if (ma->nat[n] > nalloc)
- {
- nalloc = over_alloc_dd(ma->nat[n]);
- srenew(buf,nalloc);
- }
-#ifdef GMX_MPI
- MPI_Recv(buf,ma->nat[n]*sizeof(rvec),MPI_BYTE,DDRANK(dd,n),
- n,dd->mpi_comm_all,MPI_STATUS_IGNORE);
-#endif
- a = 0;
- for(i=ma->index[n]; i<ma->index[n+1]; i++)
- {
- for(c=cgs_gl->index[ma->cg[i]]; c<cgs_gl->index[ma->cg[i]+1]; c++)
- {
- copy_rvec(buf[a++],v[c]);
- }
- }
- }
- }
- sfree(buf);
- }
-}
-
-static void get_commbuffer_counts(gmx_domdec_t *dd,
- int **counts,int **disps)
-{
- gmx_domdec_master_t *ma;
- int n;
-
- ma = dd->ma;
-
- /* Make the rvec count and displacment arrays */
- *counts = ma->ibuf;
- *disps = ma->ibuf + dd->nnodes;
- for(n=0; n<dd->nnodes; n++)
- {
- (*counts)[n] = ma->nat[n]*sizeof(rvec);
- (*disps)[n] = (n == 0 ? 0 : (*disps)[n-1] + (*counts)[n-1]);
- }
-}
-
-static void dd_collect_vec_gatherv(gmx_domdec_t *dd,
- rvec *lv,rvec *v)
-{
- gmx_domdec_master_t *ma;
- int *rcounts=NULL,*disps=NULL;
- int n,i,c,a;
- rvec *buf=NULL;
- t_block *cgs_gl;
-
- ma = dd->ma;
-
- if (DDMASTER(dd))
- {
- get_commbuffer_counts(dd,&rcounts,&disps);
-
- buf = ma->vbuf;
- }
-
- dd_gatherv(dd,dd->nat_home*sizeof(rvec),lv,rcounts,disps,buf);
-
- if (DDMASTER(dd))
- {
- cgs_gl = &dd->comm->cgs_gl;
-
- a = 0;
- for(n=0; n<dd->nnodes; n++)
- {
- for(i=ma->index[n]; i<ma->index[n+1]; i++)
- {
- for(c=cgs_gl->index[ma->cg[i]]; c<cgs_gl->index[ma->cg[i]+1]; c++)
- {
- copy_rvec(buf[a++],v[c]);
- }
- }
- }
- }
-}
-
-void dd_collect_vec(gmx_domdec_t *dd,
- t_state *state_local,rvec *lv,rvec *v)
-{
- gmx_domdec_master_t *ma;
- int n,i,c,a,nalloc=0;
- rvec *buf=NULL;
-
- dd_collect_cg(dd,state_local);
-
- if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
- {
- dd_collect_vec_sendrecv(dd,lv,v);
- }
- else
- {
- dd_collect_vec_gatherv(dd,lv,v);
- }
-}
-
-
-void dd_collect_state(gmx_domdec_t *dd,
- t_state *state_local,t_state *state)
-{
- int est,i,j,nh;
-
- nh = state->nhchainlength;
-
- if (DDMASTER(dd))
- {
- state->lambda = state_local->lambda;
- state->veta = state_local->veta;
- state->vol0 = state_local->vol0;
- copy_mat(state_local->box,state->box);
- copy_mat(state_local->boxv,state->boxv);
- copy_mat(state_local->svir_prev,state->svir_prev);
- copy_mat(state_local->fvir_prev,state->fvir_prev);
- copy_mat(state_local->pres_prev,state->pres_prev);
-
-
- for(i=0; i<state_local->ngtc; i++)
- {
- for(j=0; j<nh; j++) {
- state->nosehoover_xi[i*nh+j] = state_local->nosehoover_xi[i*nh+j];
- state->nosehoover_vxi[i*nh+j] = state_local->nosehoover_vxi[i*nh+j];
- }
- state->therm_integral[i] = state_local->therm_integral[i];
- }
- for(i=0; i<state_local->nnhpres; i++)
- {
- for(j=0; j<nh; j++) {
- state->nhpres_xi[i*nh+j] = state_local->nhpres_xi[i*nh+j];
- state->nhpres_vxi[i*nh+j] = state_local->nhpres_vxi[i*nh+j];
- }
- }
- }
- for(est=0; est<estNR; est++)
- {
- if (EST_DISTR(est) && state_local->flags & (1<<est))
- {
- switch (est) {
- case estX:
- dd_collect_vec(dd,state_local,state_local->x,state->x);
- break;
- case estV:
- dd_collect_vec(dd,state_local,state_local->v,state->v);
- break;
- case estSDX:
- dd_collect_vec(dd,state_local,state_local->sd_X,state->sd_X);
- break;
- case estCGP:
- dd_collect_vec(dd,state_local,state_local->cg_p,state->cg_p);
- break;
- case estLD_RNG:
- if (state->nrngi == 1)
- {
- if (DDMASTER(dd))
- {
- for(i=0; i<state_local->nrng; i++)
- {
- state->ld_rng[i] = state_local->ld_rng[i];
- }
- }
- }
- else
- {
- dd_gather(dd,state_local->nrng*sizeof(state->ld_rng[0]),
- state_local->ld_rng,state->ld_rng);
- }
- break;
- case estLD_RNGI:
- if (state->nrngi == 1)
- {
- if (DDMASTER(dd))
- {
- state->ld_rngi[0] = state_local->ld_rngi[0];
- }
- }
- else
- {
- dd_gather(dd,sizeof(state->ld_rngi[0]),
- state_local->ld_rngi,state->ld_rngi);
- }
- break;
- case estDISRE_INITF:
- case estDISRE_RM3TAV:
- case estORIRE_INITF:
- case estORIRE_DTAV:
- break;
- default:
- gmx_incons("Unknown state entry encountered in dd_collect_state");
- }
- }
- }
-}
-
-static void dd_realloc_fr_cg(t_forcerec *fr,int nalloc)
-{
- if (debug)
- {
- fprintf(debug,"Reallocating forcerec: currently %d, required %d, allocating %d\n",fr->cg_nalloc,nalloc,over_alloc_dd(nalloc));
- }
- fr->cg_nalloc = over_alloc_dd(nalloc);
- srenew(fr->cg_cm,fr->cg_nalloc);
- srenew(fr->cginfo,fr->cg_nalloc);
-}
-
-static void dd_realloc_state(t_state *state,rvec **f,int nalloc)
-{
- int est;
-
- if (debug)
- {
- fprintf(debug,"Reallocating state: currently %d, required %d, allocating %d\n",state->nalloc,nalloc,over_alloc_dd(nalloc));
- }
-
- state->nalloc = over_alloc_dd(nalloc);
-
- for(est=0; est<estNR; est++)
- {
- if (EST_DISTR(est) && state->flags & (1<<est))
- {
- switch(est) {
- case estX:
- srenew(state->x,state->nalloc);
- break;
- case estV:
- srenew(state->v,state->nalloc);
- break;
- case estSDX:
- srenew(state->sd_X,state->nalloc);
- break;
- case estCGP:
- srenew(state->cg_p,state->nalloc);
- break;
- case estLD_RNG:
- case estLD_RNGI:
- case estDISRE_INITF:
- case estDISRE_RM3TAV:
- case estORIRE_INITF:
- case estORIRE_DTAV:
- /* No reallocation required */
- break;
- default:
- gmx_incons("Unknown state entry encountered in dd_realloc_state");
- }
- }
- }
-
- if (f != NULL)
- {
- srenew(*f,state->nalloc);
- }
-}
-
-static void dd_distribute_vec_sendrecv(gmx_domdec_t *dd,t_block *cgs,
- rvec *v,rvec *lv)
-{
- gmx_domdec_master_t *ma;
- int n,i,c,a,nalloc=0;
- rvec *buf=NULL;
-
- if (DDMASTER(dd))
- {
- ma = dd->ma;
-
- for(n=0; n<dd->nnodes; n++)
- {
- if (n != dd->rank)
- {
- if (ma->nat[n] > nalloc)
- {
- nalloc = over_alloc_dd(ma->nat[n]);
- srenew(buf,nalloc);
- }
- /* Use lv as a temporary buffer */
- a = 0;
- for(i=ma->index[n]; i<ma->index[n+1]; i++)
- {
- for(c=cgs->index[ma->cg[i]]; c<cgs->index[ma->cg[i]+1]; c++)
- {
- copy_rvec(v[c],buf[a++]);
- }
- }
- if (a != ma->nat[n])
- {
- gmx_fatal(FARGS,"Internal error a (%d) != nat (%d)",
- a,ma->nat[n]);
- }
-
-#ifdef GMX_MPI
- MPI_Send(buf,ma->nat[n]*sizeof(rvec),MPI_BYTE,
- DDRANK(dd,n),n,dd->mpi_comm_all);
-#endif
- }
- }
- sfree(buf);
- n = DDMASTERRANK(dd);
- a = 0;
- for(i=ma->index[n]; i<ma->index[n+1]; i++)
- {
- for(c=cgs->index[ma->cg[i]]; c<cgs->index[ma->cg[i]+1]; c++)
- {
- copy_rvec(v[c],lv[a++]);
- }
- }
- }
- else
- {
-#ifdef GMX_MPI
- MPI_Recv(lv,dd->nat_home*sizeof(rvec),MPI_BYTE,DDMASTERRANK(dd),
- MPI_ANY_TAG,dd->mpi_comm_all,MPI_STATUS_IGNORE);
-#endif
- }
-}
-
-static void dd_distribute_vec_scatterv(gmx_domdec_t *dd,t_block *cgs,
- rvec *v,rvec *lv)
-{
- gmx_domdec_master_t *ma;
- int *scounts=NULL,*disps=NULL;
- int n,i,c,a,nalloc=0;
- rvec *buf=NULL;
-
- if (DDMASTER(dd))
- {
- ma = dd->ma;
-
- get_commbuffer_counts(dd,&scounts,&disps);
-
- buf = ma->vbuf;
- a = 0;
- for(n=0; n<dd->nnodes; n++)
- {
- for(i=ma->index[n]; i<ma->index[n+1]; i++)
- {
- for(c=cgs->index[ma->cg[i]]; c<cgs->index[ma->cg[i]+1]; c++)
- {
- copy_rvec(v[c],buf[a++]);
- }
- }
- }
- }
-
- dd_scatterv(dd,scounts,disps,buf,dd->nat_home*sizeof(rvec),lv);
-}
-
-static void dd_distribute_vec(gmx_domdec_t *dd,t_block *cgs,rvec *v,rvec *lv)
-{
- if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
- {
- dd_distribute_vec_sendrecv(dd,cgs,v,lv);
- }
- else
- {
- dd_distribute_vec_scatterv(dd,cgs,v,lv);
- }
-}
-
-static void dd_distribute_state(gmx_domdec_t *dd,t_block *cgs,
- t_state *state,t_state *state_local,
- rvec **f)
-{
- int i,j,ngtch,ngtcp,nh;
-
- nh = state->nhchainlength;
-
- if (DDMASTER(dd))
- {
- state_local->lambda = state->lambda;
- state_local->veta = state->veta;
- state_local->vol0 = state->vol0;
- copy_mat(state->box,state_local->box);
- copy_mat(state->box_rel,state_local->box_rel);
- copy_mat(state->boxv,state_local->boxv);
- copy_mat(state->svir_prev,state_local->svir_prev);
- copy_mat(state->fvir_prev,state_local->fvir_prev);
- for(i=0; i<state_local->ngtc; i++)
- {
- for(j=0; j<nh; j++) {
- state_local->nosehoover_xi[i*nh+j] = state->nosehoover_xi[i*nh+j];
- state_local->nosehoover_vxi[i*nh+j] = state->nosehoover_vxi[i*nh+j];
- }
- state_local->therm_integral[i] = state->therm_integral[i];
- }
- for(i=0; i<state_local->nnhpres; i++)
- {
- for(j=0; j<nh; j++) {
- state_local->nhpres_xi[i*nh+j] = state->nhpres_xi[i*nh+j];
- state_local->nhpres_vxi[i*nh+j] = state->nhpres_vxi[i*nh+j];
- }
- }
- }
- dd_bcast(dd,sizeof(real),&state_local->lambda);
- dd_bcast(dd,sizeof(real),&state_local->veta);
- dd_bcast(dd,sizeof(real),&state_local->vol0);
- dd_bcast(dd,sizeof(state_local->box),state_local->box);
- dd_bcast(dd,sizeof(state_local->box_rel),state_local->box_rel);
- dd_bcast(dd,sizeof(state_local->boxv),state_local->boxv);
- dd_bcast(dd,sizeof(state_local->svir_prev),state_local->svir_prev);
- dd_bcast(dd,sizeof(state_local->fvir_prev),state_local->fvir_prev);
- dd_bcast(dd,((state_local->ngtc*nh)*sizeof(double)),state_local->nosehoover_xi);
- dd_bcast(dd,((state_local->ngtc*nh)*sizeof(double)),state_local->nosehoover_vxi);
- dd_bcast(dd,state_local->ngtc*sizeof(double),state_local->therm_integral);
- dd_bcast(dd,((state_local->nnhpres*nh)*sizeof(double)),state_local->nhpres_xi);
- dd_bcast(dd,((state_local->nnhpres*nh)*sizeof(double)),state_local->nhpres_vxi);
-
- if (dd->nat_home > state_local->nalloc)
- {
- dd_realloc_state(state_local,f,dd->nat_home);
- }
- for(i=0; i<estNR; i++)
- {
- if (EST_DISTR(i) && state_local->flags & (1<<i))
- {
- switch (i) {
- case estX:
- dd_distribute_vec(dd,cgs,state->x,state_local->x);
- break;
- case estV:
- dd_distribute_vec(dd,cgs,state->v,state_local->v);
- break;
- case estSDX:
- dd_distribute_vec(dd,cgs,state->sd_X,state_local->sd_X);
- break;
- case estCGP:
- dd_distribute_vec(dd,cgs,state->cg_p,state_local->cg_p);
- break;
- case estLD_RNG:
- if (state->nrngi == 1)
- {
- dd_bcastc(dd,
- state_local->nrng*sizeof(state_local->ld_rng[0]),
- state->ld_rng,state_local->ld_rng);
- }
- else
- {
- dd_scatter(dd,
- state_local->nrng*sizeof(state_local->ld_rng[0]),
- state->ld_rng,state_local->ld_rng);
- }
- break;
- case estLD_RNGI:
- if (state->nrngi == 1)
- {
- dd_bcastc(dd,sizeof(state_local->ld_rngi[0]),
- state->ld_rngi,state_local->ld_rngi);
- }
- else
- {
- dd_scatter(dd,sizeof(state_local->ld_rngi[0]),
- state->ld_rngi,state_local->ld_rngi);
- }
- break;
- case estDISRE_INITF:
- case estDISRE_RM3TAV:
- case estORIRE_INITF:
- case estORIRE_DTAV:
- /* Not implemented yet */
- break;
- default:
- gmx_incons("Unknown state entry encountered in dd_distribute_state");
- }
- }
- }
-}
-
-static char dim2char(int dim)
-{
- char c='?';
-
- switch (dim)
- {
- case XX: c = 'X'; break;
- case YY: c = 'Y'; break;
- case ZZ: c = 'Z'; break;
- default: gmx_fatal(FARGS,"Unknown dim %d",dim);
- }
-
- return c;
-}
-
-static void write_dd_grid_pdb(const char *fn,gmx_large_int_t step,
- gmx_domdec_t *dd,matrix box,gmx_ddbox_t *ddbox)
-{
- rvec grid_s[2],*grid_r=NULL,cx,r;
- char fname[STRLEN],format[STRLEN],buf[22];
- FILE *out;
- int a,i,d,z,y,x;
- matrix tric;
- real vol;
-
- copy_rvec(dd->comm->cell_x0,grid_s[0]);
- copy_rvec(dd->comm->cell_x1,grid_s[1]);
-
- if (DDMASTER(dd))
- {
- snew(grid_r,2*dd->nnodes);
- }
-
- dd_gather(dd,2*sizeof(rvec),grid_s[0],DDMASTER(dd) ? grid_r[0] : NULL);
-
- if (DDMASTER(dd))
- {
- for(d=0; d<DIM; d++)
- {
- for(i=0; i<DIM; i++)
- {
- if (d == i)
- {
- tric[d][i] = 1;
- }
- else
- {
- if (dd->nc[d] > 1 && d < ddbox->npbcdim)
- {
- tric[d][i] = box[i][d]/box[i][i];
- }
- else
- {
- tric[d][i] = 0;
- }
- }
- }
- }
- sprintf(fname,"%s_%s.pdb",fn,gmx_step_str(step,buf));
- sprintf(format,"%s%s\n",pdbformat,"%6.2f%6.2f");
- out = gmx_fio_fopen(fname,"w");
- gmx_write_pdb_box(out,dd->bScrewPBC ? epbcSCREW : epbcXYZ,box);
- a = 1;
- for(i=0; i<dd->nnodes; i++)
- {
- vol = dd->nnodes/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
- for(d=0; d<DIM; d++)
- {
- vol *= grid_r[i*2+1][d] - grid_r[i*2][d];
- }
- for(z=0; z<2; z++)
- {
- for(y=0; y<2; y++)
- {
- for(x=0; x<2; x++)
- {
- cx[XX] = grid_r[i*2+x][XX];
- cx[YY] = grid_r[i*2+y][YY];
- cx[ZZ] = grid_r[i*2+z][ZZ];
- mvmul(tric,cx,r);
- fprintf(out,format,"ATOM",a++,"CA","GLY",' ',1+i,
- 10*r[XX],10*r[YY],10*r[ZZ],1.0,vol);
- }
- }
- }
- for(d=0; d<DIM; d++)
- {
- for(x=0; x<4; x++)
- {
- switch(d)
- {
- case 0: y = 1 + i*8 + 2*x; break;
- case 1: y = 1 + i*8 + 2*x - (x % 2); break;
- case 2: y = 1 + i*8 + x; break;
- }
- fprintf(out,"%6s%5d%5d\n","CONECT",y,y+(1<<d));
- }
- }
- }
- gmx_fio_fclose(out);
- sfree(grid_r);
- }
-}
-
-void write_dd_pdb(const char *fn,gmx_large_int_t step,const char *title,
- gmx_mtop_t *mtop,t_commrec *cr,
- int natoms,rvec x[],matrix box)
-{
- char fname[STRLEN],format[STRLEN],format4[STRLEN],buf[22];
- FILE *out;
- int i,ii,resnr,c;
- char *atomname,*resname;
- real b;
- gmx_domdec_t *dd;
-
- dd = cr->dd;
- if (natoms == -1)
- {
- natoms = dd->comm->nat[ddnatVSITE];
- }
-
- sprintf(fname,"%s_%s_n%d.pdb",fn,gmx_step_str(step,buf),cr->sim_nodeid);
-
- sprintf(format,"%s%s\n",pdbformat,"%6.2f%6.2f");
- sprintf(format4,"%s%s\n",pdbformat4,"%6.2f%6.2f");
-
- out = gmx_fio_fopen(fname,"w");
-
- fprintf(out,"TITLE %s\n",title);
- gmx_write_pdb_box(out,dd->bScrewPBC ? epbcSCREW : epbcXYZ,box);
- for(i=0; i<natoms; i++)
- {
- ii = dd->gatindex[i];
- gmx_mtop_atominfo_global(mtop,ii,&atomname,&resnr,&resname);
- if (i < dd->comm->nat[ddnatZONE])
- {
- c = 0;
- while (i >= dd->cgindex[dd->comm->zones.cg_range[c+1]])
- {
- c++;
- }
- b = c;
- }
- else if (i < dd->comm->nat[ddnatVSITE])
- {
- b = dd->comm->zones.n;
- }
- else
- {
- b = dd->comm->zones.n + 1;
- }
- fprintf(out,strlen(atomname)<4 ? format : format4,
- "ATOM",(ii+1)%100000,
- atomname,resname,' ',resnr%10000,' ',
- 10*x[i][XX],10*x[i][YY],10*x[i][ZZ],1.0,b);
- }
- fprintf(out,"TER\n");
-
- gmx_fio_fclose(out);
-}
-
-real dd_cutoff_mbody(gmx_domdec_t *dd)
-{
- gmx_domdec_comm_t *comm;
- int di;
- real r;
-
- comm = dd->comm;
-
- r = -1;
- if (comm->bInterCGBondeds)
- {
- if (comm->cutoff_mbody > 0)
- {
- r = comm->cutoff_mbody;
- }
- else
- {
- /* cutoff_mbody=0 means we do not have DLB */
- r = comm->cellsize_min[dd->dim[0]];
- for(di=1; di<dd->ndim; di++)
- {
- r = min(r,comm->cellsize_min[dd->dim[di]]);
- }
- if (comm->bBondComm)
- {
- r = max(r,comm->cutoff_mbody);
- }
- else
- {
- r = min(r,comm->cutoff);
- }
- }
- }
-
- return r;
-}
-
-real dd_cutoff_twobody(gmx_domdec_t *dd)
-{
- real r_mb;
-
- r_mb = dd_cutoff_mbody(dd);
-
- return max(dd->comm->cutoff,r_mb);
-}
-
-
-static void dd_cart_coord2pmecoord(gmx_domdec_t *dd,ivec coord,ivec coord_pme)
-{
- int nc,ntot;
-
- nc = dd->nc[dd->comm->cartpmedim];
- ntot = dd->comm->ntot[dd->comm->cartpmedim];
- copy_ivec(coord,coord_pme);
- coord_pme[dd->comm->cartpmedim] =
- nc + (coord[dd->comm->cartpmedim]*(ntot - nc) + (ntot - nc)/2)/nc;
-}
-
-static int low_ddindex2pmeindex(int ndd,int npme,int ddindex)
-{
- /* Here we assign a PME node to communicate with this DD node
- * by assuming that the major index of both is x.
- * We add cr->npmenodes/2 to obtain an even distribution.
- */
- return (ddindex*npme + npme/2)/ndd;
-}
-
-static int ddindex2pmeindex(const gmx_domdec_t *dd,int ddindex)
-{
- return low_ddindex2pmeindex(dd->nnodes,dd->comm->npmenodes,ddindex);
-}
-
-static int cr_ddindex2pmeindex(const t_commrec *cr,int ddindex)
-{
- return low_ddindex2pmeindex(cr->dd->nnodes,cr->npmenodes,ddindex);
-}
-
-static int *dd_pmenodes(t_commrec *cr)
-{
- int *pmenodes;
- int n,i,p0,p1;
-
- snew(pmenodes,cr->npmenodes);
- n = 0;
- for(i=0; i<cr->dd->nnodes; i++) {
- p0 = cr_ddindex2pmeindex(cr,i);
- p1 = cr_ddindex2pmeindex(cr,i+1);
- if (i+1 == cr->dd->nnodes || p1 > p0) {
- if (debug)
- fprintf(debug,"pmenode[%d] = %d\n",n,i+1+n);
- pmenodes[n] = i + 1 + n;
- n++;
- }
- }
-
- return pmenodes;
-}
-
-static int gmx_ddcoord2pmeindex(t_commrec *cr,int x,int y,int z)
-{
- gmx_domdec_t *dd;
- ivec coords,coords_pme,nc;
- int slab;
-
- dd = cr->dd;
- /*
- if (dd->comm->bCartesian) {
- gmx_ddindex2xyz(dd->nc,ddindex,coords);
- dd_coords2pmecoords(dd,coords,coords_pme);
- copy_ivec(dd->ntot,nc);
- nc[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
- coords_pme[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
-
- slab = (coords_pme[XX]*nc[YY] + coords_pme[YY])*nc[ZZ] + coords_pme[ZZ];
- } else {
- slab = (ddindex*cr->npmenodes + cr->npmenodes/2)/dd->nnodes;
- }
- */
- coords[XX] = x;
- coords[YY] = y;
- coords[ZZ] = z;
- slab = ddindex2pmeindex(dd,dd_index(dd->nc,coords));
-
- return slab;
-}
-
-static int ddcoord2simnodeid(t_commrec *cr,int x,int y,int z)
-{
- gmx_domdec_comm_t *comm;
- ivec coords;
- int ddindex,nodeid=-1;
-
- comm = cr->dd->comm;
-
- coords[XX] = x;
- coords[YY] = y;
- coords[ZZ] = z;
- if (comm->bCartesianPP_PME)
- {
-#ifdef GMX_MPI
- MPI_Cart_rank(cr->mpi_comm_mysim,coords,&nodeid);
-#endif
- }
- else
- {
- ddindex = dd_index(cr->dd->nc,coords);
- if (comm->bCartesianPP)
- {
- nodeid = comm->ddindex2simnodeid[ddindex];
- }
- else
- {
- if (comm->pmenodes)
- {
- nodeid = ddindex + gmx_ddcoord2pmeindex(cr,x,y,z);
- }
- else
- {
- nodeid = ddindex;
- }
- }
- }
-
- return nodeid;
-}
-
-static int dd_simnode2pmenode(t_commrec *cr,int sim_nodeid)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- ivec coord,coord_pme;
- int i;
- int pmenode=-1;
-
- dd = cr->dd;
- comm = dd->comm;
-
- /* This assumes a uniform x domain decomposition grid cell size */
- if (comm->bCartesianPP_PME)
- {
-#ifdef GMX_MPI
- MPI_Cart_coords(cr->mpi_comm_mysim,sim_nodeid,DIM,coord);
- if (coord[comm->cartpmedim] < dd->nc[comm->cartpmedim])
- {
- /* This is a PP node */
- dd_cart_coord2pmecoord(dd,coord,coord_pme);
- MPI_Cart_rank(cr->mpi_comm_mysim,coord_pme,&pmenode);
- }
-#endif
- }
- else if (comm->bCartesianPP)
- {
- if (sim_nodeid < dd->nnodes)
- {
- pmenode = dd->nnodes + ddindex2pmeindex(dd,sim_nodeid);
- }
- }
- else
- {
- /* This assumes DD cells with identical x coordinates
- * are numbered sequentially.
- */
- if (dd->comm->pmenodes == NULL)
- {
- if (sim_nodeid < dd->nnodes)
- {
- /* The DD index equals the nodeid */
- pmenode = dd->nnodes + ddindex2pmeindex(dd,sim_nodeid);
- }
- }
- else
- {
- i = 0;
- while (sim_nodeid > dd->comm->pmenodes[i])
- {
- i++;
- }
- if (sim_nodeid < dd->comm->pmenodes[i])
- {
- pmenode = dd->comm->pmenodes[i];
- }
- }
- }
-
- return pmenode;
-}
-
-gmx_bool gmx_pmeonlynode(t_commrec *cr,int sim_nodeid)
-{
- gmx_bool bPMEOnlyNode;
-
- if (DOMAINDECOMP(cr))
- {
- bPMEOnlyNode = (dd_simnode2pmenode(cr,sim_nodeid) == -1);
- }
- else
- {
- bPMEOnlyNode = FALSE;
- }
-
- return bPMEOnlyNode;
-}
-
-void get_pme_ddnodes(t_commrec *cr,int pmenodeid,
- int *nmy_ddnodes,int **my_ddnodes,int *node_peer)
-{
- gmx_domdec_t *dd;
- int x,y,z;
- ivec coord,coord_pme;
-
- dd = cr->dd;
-
- snew(*my_ddnodes,(dd->nnodes+cr->npmenodes-1)/cr->npmenodes);
-
- *nmy_ddnodes = 0;
- for(x=0; x<dd->nc[XX]; x++)
- {
- for(y=0; y<dd->nc[YY]; y++)
- {
- for(z=0; z<dd->nc[ZZ]; z++)
- {
- if (dd->comm->bCartesianPP_PME)
- {
- coord[XX] = x;
- coord[YY] = y;
- coord[ZZ] = z;
- dd_cart_coord2pmecoord(dd,coord,coord_pme);
- if (dd->ci[XX] == coord_pme[XX] &&
- dd->ci[YY] == coord_pme[YY] &&
- dd->ci[ZZ] == coord_pme[ZZ])
- (*my_ddnodes)[(*nmy_ddnodes)++] = ddcoord2simnodeid(cr,x,y,z);
- }
- else
- {
- /* The slab corresponds to the nodeid in the PME group */
- if (gmx_ddcoord2pmeindex(cr,x,y,z) == pmenodeid)
- {
- (*my_ddnodes)[(*nmy_ddnodes)++] = ddcoord2simnodeid(cr,x,y,z);
- }
- }
- }
- }
- }
-
- /* The last PP-only node is the peer node */
- *node_peer = (*my_ddnodes)[*nmy_ddnodes-1];
-
- if (debug)
- {
- fprintf(debug,"Receive coordinates from PP nodes:");
- for(x=0; x<*nmy_ddnodes; x++)
- {
- fprintf(debug," %d",(*my_ddnodes)[x]);
- }
- fprintf(debug,"\n");
- }
-}
-
-static gmx_bool receive_vir_ener(t_commrec *cr)
-{
- gmx_domdec_comm_t *comm;
- int pmenode,coords[DIM],rank;
- gmx_bool bReceive;
-
- bReceive = TRUE;
- if (cr->npmenodes < cr->dd->nnodes)
- {
- comm = cr->dd->comm;
- if (comm->bCartesianPP_PME)
- {
- pmenode = dd_simnode2pmenode(cr,cr->sim_nodeid);
-#ifdef GMX_MPI
- MPI_Cart_coords(cr->mpi_comm_mysim,cr->sim_nodeid,DIM,coords);
- coords[comm->cartpmedim]++;
- if (coords[comm->cartpmedim] < cr->dd->nc[comm->cartpmedim])
- {
- MPI_Cart_rank(cr->mpi_comm_mysim,coords,&rank);
- if (dd_simnode2pmenode(cr,rank) == pmenode)
- {
- /* This is not the last PP node for pmenode */
- bReceive = FALSE;
- }
- }
-#endif
- }
- else
- {
- pmenode = dd_simnode2pmenode(cr,cr->sim_nodeid);
- if (cr->sim_nodeid+1 < cr->nnodes &&
- dd_simnode2pmenode(cr,cr->sim_nodeid+1) == pmenode)
- {
- /* This is not the last PP node for pmenode */
- bReceive = FALSE;
- }
- }
- }
-
- return bReceive;
-}
-
-static void set_zones_ncg_home(gmx_domdec_t *dd)
-{
- gmx_domdec_zones_t *zones;
- int i;
-
- zones = &dd->comm->zones;
-
- zones->cg_range[0] = 0;
- for(i=1; i<zones->n+1; i++)
- {
- zones->cg_range[i] = dd->ncg_home;
- }
-}
-
-static void rebuild_cgindex(gmx_domdec_t *dd,int *gcgs_index,t_state *state)
-{
- int nat,i,*ind,*dd_cg_gl,*cgindex,cg_gl;
-
- ind = state->cg_gl;
- dd_cg_gl = dd->index_gl;
- cgindex = dd->cgindex;
- nat = 0;
- cgindex[0] = nat;
- for(i=0; i<state->ncg_gl; i++)
- {
- cgindex[i] = nat;
- cg_gl = ind[i];
- dd_cg_gl[i] = cg_gl;
- nat += gcgs_index[cg_gl+1] - gcgs_index[cg_gl];
- }
- cgindex[i] = nat;
-
- dd->ncg_home = state->ncg_gl;
- dd->nat_home = nat;
-
- set_zones_ncg_home(dd);
-}
-
-static int ddcginfo(const cginfo_mb_t *cginfo_mb,int cg)
-{
- while (cg >= cginfo_mb->cg_end)
- {
- cginfo_mb++;
- }
-
- return cginfo_mb->cginfo[(cg - cginfo_mb->cg_start) % cginfo_mb->cg_mod];
-}
-
-static void dd_set_cginfo(int *index_gl,int cg0,int cg1,
- t_forcerec *fr,char *bLocalCG)
-{
- cginfo_mb_t *cginfo_mb;
- int *cginfo;
- int cg;
-
- if (fr != NULL)
- {
- cginfo_mb = fr->cginfo_mb;
- cginfo = fr->cginfo;
-
- for(cg=cg0; cg<cg1; cg++)
- {
- cginfo[cg] = ddcginfo(cginfo_mb,index_gl[cg]);
- }
- }
-
- if (bLocalCG != NULL)
- {
- for(cg=cg0; cg<cg1; cg++)
- {
- bLocalCG[index_gl[cg]] = TRUE;
- }
- }
-}
-
-static void make_dd_indices(gmx_domdec_t *dd,int *gcgs_index,int cg_start)
-{
- int nzone,zone,zone1,cg0,cg,cg_gl,a,a_gl;
- int *zone2cg,*zone_ncg1,*index_gl,*gatindex;
- gmx_ga2la_t *ga2la;
- char *bLocalCG;
-
- bLocalCG = dd->comm->bLocalCG;
-
- if (dd->nat_tot > dd->gatindex_nalloc)
- {
- dd->gatindex_nalloc = over_alloc_dd(dd->nat_tot);
- srenew(dd->gatindex,dd->gatindex_nalloc);
- }
-
- nzone = dd->comm->zones.n;
- zone2cg = dd->comm->zones.cg_range;
- zone_ncg1 = dd->comm->zone_ncg1;
- index_gl = dd->index_gl;
- gatindex = dd->gatindex;
-
- if (zone2cg[1] != dd->ncg_home)
- {
- gmx_incons("dd->ncg_zone is not up to date");
- }
-
- /* Make the local to global and global to local atom index */
- a = dd->cgindex[cg_start];
- for(zone=0; zone<nzone; zone++)
- {
- if (zone == 0)
- {
- cg0 = cg_start;
- }
- else
- {
- cg0 = zone2cg[zone];
- }
- for(cg=cg0; cg<zone2cg[zone+1]; cg++)
- {
- zone1 = zone;
- if (cg - cg0 >= zone_ncg1[zone])
- {
- /* Signal that this cg is from more than one zone away */
- zone1 += nzone;
- }
- cg_gl = index_gl[cg];
- for(a_gl=gcgs_index[cg_gl]; a_gl<gcgs_index[cg_gl+1]; a_gl++)
- {
- gatindex[a] = a_gl;
- ga2la_set(dd->ga2la,a_gl,a,zone1);
- a++;
- }
- }
- }
-}
-
-static int check_bLocalCG(gmx_domdec_t *dd,int ncg_sys,const char *bLocalCG,
- const char *where)
-{
- int ncg,i,ngl,nerr;
-
- nerr = 0;
- if (bLocalCG == NULL)
- {
- return nerr;
- }
- for(i=0; i<dd->ncg_tot; i++)
- {
- if (!bLocalCG[dd->index_gl[i]])
- {
- fprintf(stderr,
- "DD node %d, %s: cg %d, global cg %d is not marked in bLocalCG (ncg_home %d)\n",dd->rank,where,i+1,dd->index_gl[i]+1,dd->ncg_home);
- nerr++;
- }
- }
- ngl = 0;
- for(i=0; i<ncg_sys; i++)
- {
- if (bLocalCG[i])
- {
- ngl++;
- }
- }
- if (ngl != dd->ncg_tot)
- {
- fprintf(stderr,"DD node %d, %s: In bLocalCG %d cgs are marked as local, whereas there are %d\n",dd->rank,where,ngl,dd->ncg_tot);
- nerr++;
- }
-
- return nerr;
-}
-
-static void check_index_consistency(gmx_domdec_t *dd,
- int natoms_sys,int ncg_sys,
- const char *where)
-{
- int nerr,ngl,i,a,cell;
- int *have;
-
- nerr = 0;
-
- if (dd->comm->DD_debug > 1)
- {
- snew(have,natoms_sys);
- for(a=0; a<dd->nat_tot; a++)
- {
- if (have[dd->gatindex[a]] > 0)
- {
- fprintf(stderr,"DD node %d: global atom %d occurs twice: index %d and %d\n",dd->rank,dd->gatindex[a]+1,have[dd->gatindex[a]],a+1);
- }
- else
- {
- have[dd->gatindex[a]] = a + 1;
- }
- }
- sfree(have);
- }
-
- snew(have,dd->nat_tot);
-
- ngl = 0;
- for(i=0; i<natoms_sys; i++)
- {
- if (ga2la_get(dd->ga2la,i,&a,&cell))
- {
- if (a >= dd->nat_tot)
- {
- fprintf(stderr,"DD node %d: global atom %d marked as local atom %d, which is larger than nat_tot (%d)\n",dd->rank,i+1,a+1,dd->nat_tot);
- nerr++;
- }
- else
- {
- have[a] = 1;
- if (dd->gatindex[a] != i)
- {
- fprintf(stderr,"DD node %d: global atom %d marked as local atom %d, which has global atom index %d\n",dd->rank,i+1,a+1,dd->gatindex[a]+1);
- nerr++;
- }
- }
- ngl++;
- }
- }
- if (ngl != dd->nat_tot)
- {
- fprintf(stderr,
- "DD node %d, %s: %d global atom indices, %d local atoms\n",
- dd->rank,where,ngl,dd->nat_tot);
- }
- for(a=0; a<dd->nat_tot; a++)
- {
- if (have[a] == 0)
- {
- fprintf(stderr,
- "DD node %d, %s: local atom %d, global %d has no global index\n",
- dd->rank,where,a+1,dd->gatindex[a]+1);
- }
- }
- sfree(have);
-
- nerr += check_bLocalCG(dd,ncg_sys,dd->comm->bLocalCG,where);
-
- if (nerr > 0) {
- gmx_fatal(FARGS,"DD node %d, %s: %d atom/cg index inconsistencies",
- dd->rank,where,nerr);
- }
-}
-
-static void clear_dd_indices(gmx_domdec_t *dd,int cg_start,int a_start)
-{
- int i;
- char *bLocalCG;
-
- if (a_start == 0)
- {
- /* Clear the whole list without searching */
- ga2la_clear(dd->ga2la);
- }
- else
- {
- for(i=a_start; i<dd->nat_tot; i++)
- {
- ga2la_del(dd->ga2la,dd->gatindex[i]);
- }
- }
-
- bLocalCG = dd->comm->bLocalCG;
- if (bLocalCG)
- {
- for(i=cg_start; i<dd->ncg_tot; i++)
- {
- bLocalCG[dd->index_gl[i]] = FALSE;
- }
- }
-
- dd_clear_local_vsite_indices(dd);
-
- if (dd->constraints)
- {
- dd_clear_local_constraint_indices(dd);
- }
-}
-
-static real grid_jump_limit(gmx_domdec_comm_t *comm,int dim_ind)
-{
- real grid_jump_limit;
-
- /* The distance between the boundaries of cells at distance
- * x+-1,y+-1 or y+-1,z+-1 is limited by the cut-off restrictions
- * and by the fact that cells should not be shifted by more than
- * half their size, such that cg's only shift by one cell
- * at redecomposition.
- */
- grid_jump_limit = comm->cellsize_limit;
- if (!comm->bVacDLBNoLimit)
- {
- grid_jump_limit = max(grid_jump_limit,
- comm->cutoff/comm->cd[dim_ind].np);
- }
-
- return grid_jump_limit;
-}
-
-static void check_grid_jump(gmx_large_int_t step,gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
-{
- gmx_domdec_comm_t *comm;
- int d,dim;
- real limit,bfac;
-
- comm = dd->comm;
-
- for(d=1; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- limit = grid_jump_limit(comm,d);
- bfac = ddbox->box_size[dim];
- if (ddbox->tric_dir[dim])
- {
- bfac *= ddbox->skew_fac[dim];
- }
- if ((comm->cell_f1[d] - comm->cell_f_max0[d])*bfac < limit ||
- (comm->cell_f0[d] - comm->cell_f_min1[d])*bfac > -limit)
- {
- char buf[22];
- gmx_fatal(FARGS,"Step %s: The domain decomposition grid has shifted too much in the %c-direction around cell %d %d %d\n",
- gmx_step_str(step,buf),
- dim2char(dim),dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
- }
- }
-}
-
-static int dd_load_count(gmx_domdec_comm_t *comm)
-{
- return (comm->eFlop ? comm->flop_n : comm->cycl_n[ddCyclF]);
-}
-
-static float dd_force_load(gmx_domdec_comm_t *comm)
-{
- float load;
-
- if (comm->eFlop)
- {
- load = comm->flop;
- if (comm->eFlop > 1)
- {
- load *= 1.0 + (comm->eFlop - 1)*(0.1*rand()/RAND_MAX - 0.05);
- }
- }
- else
- {
- load = comm->cycl[ddCyclF];
- if (comm->cycl_n[ddCyclF] > 1)
- {
- /* Subtract the maximum of the last n cycle counts
- * to get rid of possible high counts due to other soures,
- * for instance system activity, that would otherwise
- * affect the dynamic load balancing.
- */
- load -= comm->cycl_max[ddCyclF];
- }
- }
-
- return load;
-}
-
-static void set_slb_pme_dim_f(gmx_domdec_t *dd,int dim,real **dim_f)
-{
- gmx_domdec_comm_t *comm;
- int i;
-
- comm = dd->comm;
-
- snew(*dim_f,dd->nc[dim]+1);
- (*dim_f)[0] = 0;
- for(i=1; i<dd->nc[dim]; i++)
- {
- if (comm->slb_frac[dim])
- {
- (*dim_f)[i] = (*dim_f)[i-1] + comm->slb_frac[dim][i-1];
- }
- else
- {
- (*dim_f)[i] = (real)i/(real)dd->nc[dim];
- }
- }
- (*dim_f)[dd->nc[dim]] = 1;
-}
-
-static void init_ddpme(gmx_domdec_t *dd,gmx_ddpme_t *ddpme,int dimind)
-{
- int pmeindex,slab,nso,i;
- ivec xyz;
-
- if (dimind == 0 && dd->dim[0] == YY && dd->comm->npmenodes_x == 1)
- {
- ddpme->dim = YY;
- }
- else
- {
- ddpme->dim = dimind;
- }
- ddpme->dim_match = (ddpme->dim == dd->dim[dimind]);
-
- ddpme->nslab = (ddpme->dim == 0 ?
- dd->comm->npmenodes_x :
- dd->comm->npmenodes_y);
-
- if (ddpme->nslab <= 1)
- {
- return;
- }
-
- nso = dd->comm->npmenodes/ddpme->nslab;
- /* Determine for each PME slab the PP location range for dimension dim */
- snew(ddpme->pp_min,ddpme->nslab);
- snew(ddpme->pp_max,ddpme->nslab);
- for(slab=0; slab<ddpme->nslab; slab++) {
- ddpme->pp_min[slab] = dd->nc[dd->dim[dimind]] - 1;
- ddpme->pp_max[slab] = 0;
- }
- for(i=0; i<dd->nnodes; i++) {
- ddindex2xyz(dd->nc,i,xyz);
- /* For y only use our y/z slab.
- * This assumes that the PME x grid size matches the DD grid size.
- */
- if (dimind == 0 || xyz[XX] == dd->ci[XX]) {
- pmeindex = ddindex2pmeindex(dd,i);
- if (dimind == 0) {
- slab = pmeindex/nso;
- } else {
- slab = pmeindex % ddpme->nslab;
- }
- ddpme->pp_min[slab] = min(ddpme->pp_min[slab],xyz[dimind]);
- ddpme->pp_max[slab] = max(ddpme->pp_max[slab],xyz[dimind]);
- }
- }
-
- set_slb_pme_dim_f(dd,ddpme->dim,&ddpme->slb_dim_f);
-}
-
-int dd_pme_maxshift_x(gmx_domdec_t *dd)
-{
- if (dd->comm->ddpme[0].dim == XX)
- {
- return dd->comm->ddpme[0].maxshift;
- }
- else
- {
- return 0;
- }
-}
-
-int dd_pme_maxshift_y(gmx_domdec_t *dd)
-{
- if (dd->comm->ddpme[0].dim == YY)
- {
- return dd->comm->ddpme[0].maxshift;
- }
- else if (dd->comm->npmedecompdim >= 2 && dd->comm->ddpme[1].dim == YY)
- {
- return dd->comm->ddpme[1].maxshift;
- }
- else
- {
- return 0;
- }
-}
-
-static void set_pme_maxshift(gmx_domdec_t *dd,gmx_ddpme_t *ddpme,
- gmx_bool bUniform,gmx_ddbox_t *ddbox,real *cell_f)
-{
- gmx_domdec_comm_t *comm;
- int nc,ns,s;
- int *xmin,*xmax;
- real range,pme_boundary;
- int sh;
-
- comm = dd->comm;
- nc = dd->nc[ddpme->dim];
- ns = ddpme->nslab;
-
- if (!ddpme->dim_match)
- {
- /* PP decomposition is not along dim: the worst situation */
- sh = ns/2;
- }
- else if (ns <= 3 || (bUniform && ns == nc))
- {
- /* The optimal situation */
- sh = 1;
- }
- else
- {
- /* We need to check for all pme nodes which nodes they
- * could possibly need to communicate with.
- */
- xmin = ddpme->pp_min;
- xmax = ddpme->pp_max;
- /* Allow for atoms to be maximally 2/3 times the cut-off
- * out of their DD cell. This is a reasonable balance between
- * between performance and support for most charge-group/cut-off
- * combinations.
- */
- range = 2.0/3.0*comm->cutoff/ddbox->box_size[ddpme->dim];
- /* Avoid extra communication when we are exactly at a boundary */
- range *= 0.999;
-
- sh = 1;
- for(s=0; s<ns; s++)
- {
- /* PME slab s spreads atoms between box frac. s/ns and (s+1)/ns */
- pme_boundary = (real)s/ns;
- while (sh+1 < ns &&
- ((s-(sh+1) >= 0 &&
- cell_f[xmax[s-(sh+1) ]+1] + range > pme_boundary) ||
- (s-(sh+1) < 0 &&
- cell_f[xmax[s-(sh+1)+ns]+1] - 1 + range > pme_boundary)))
- {
- sh++;
- }
- pme_boundary = (real)(s+1)/ns;
- while (sh+1 < ns &&
- ((s+(sh+1) < ns &&
- cell_f[xmin[s+(sh+1) ] ] - range < pme_boundary) ||
- (s+(sh+1) >= ns &&
- cell_f[xmin[s+(sh+1)-ns] ] + 1 - range < pme_boundary)))
- {
- sh++;
- }
- }
- }
-
- ddpme->maxshift = sh;
-
- if (debug)
- {
- fprintf(debug,"PME slab communication range for dim %d is %d\n",
- ddpme->dim,ddpme->maxshift);
- }
-}
-
-static void check_box_size(gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
-{
- int d,dim;
-
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- if (dim < ddbox->nboundeddim &&
- ddbox->box_size[dim]*ddbox->skew_fac[dim] <
- dd->nc[dim]*dd->comm->cellsize_limit*DD_CELL_MARGIN)
- {
- gmx_fatal(FARGS,"The %c-size of the box (%f) times the triclinic skew factor (%f) is smaller than the number of DD cells (%d) times the smallest allowed cell size (%f)\n",
- dim2char(dim),ddbox->box_size[dim],ddbox->skew_fac[dim],
- dd->nc[dim],dd->comm->cellsize_limit);
- }
- }
-}
-
-static void set_dd_cell_sizes_slb(gmx_domdec_t *dd,gmx_ddbox_t *ddbox,
- gmx_bool bMaster,ivec npulse)
-{
- gmx_domdec_comm_t *comm;
- int d,j;
- rvec cellsize_min;
- real *cell_x,cell_dx,cellsize;
-
- comm = dd->comm;
-
- for(d=0; d<DIM; d++)
- {
- cellsize_min[d] = ddbox->box_size[d]*ddbox->skew_fac[d];
- npulse[d] = 1;
- if (dd->nc[d] == 1 || comm->slb_frac[d] == NULL)
- {
- /* Uniform grid */
- cell_dx = ddbox->box_size[d]/dd->nc[d];
- if (bMaster)
- {
- for(j=0; j<dd->nc[d]+1; j++)
- {
- dd->ma->cell_x[d][j] = ddbox->box0[d] + j*cell_dx;
- }
- }
- else
- {
- comm->cell_x0[d] = ddbox->box0[d] + (dd->ci[d] )*cell_dx;
- comm->cell_x1[d] = ddbox->box0[d] + (dd->ci[d]+1)*cell_dx;
- }
- cellsize = cell_dx*ddbox->skew_fac[d];
- while (cellsize*npulse[d] < comm->cutoff && npulse[d] < dd->nc[d]-1)
- {
- npulse[d]++;
- }
- cellsize_min[d] = cellsize;
- }
- else
- {
- /* Statically load balanced grid */
- /* Also when we are not doing a master distribution we determine
- * all cell borders in a loop to obtain identical values
- * to the master distribution case and to determine npulse.
- */
- if (bMaster)
- {
- cell_x = dd->ma->cell_x[d];
- }
- else
- {
- snew(cell_x,dd->nc[d]+1);
- }
- cell_x[0] = ddbox->box0[d];
- for(j=0; j<dd->nc[d]; j++)
- {
- cell_dx = ddbox->box_size[d]*comm->slb_frac[d][j];
- cell_x[j+1] = cell_x[j] + cell_dx;
- cellsize = cell_dx*ddbox->skew_fac[d];
- while (cellsize*npulse[d] < comm->cutoff &&
- npulse[d] < dd->nc[d]-1)
- {
- npulse[d]++;
- }
- cellsize_min[d] = min(cellsize_min[d],cellsize);
- }
- if (!bMaster)
- {
- comm->cell_x0[d] = cell_x[dd->ci[d]];
- comm->cell_x1[d] = cell_x[dd->ci[d]+1];
- sfree(cell_x);
- }
- }
- /* The following limitation is to avoid that a cell would receive
- * some of its own home charge groups back over the periodic boundary.
- * Double charge groups cause trouble with the global indices.
- */
- if (d < ddbox->npbcdim &&
- dd->nc[d] > 1 && npulse[d] >= dd->nc[d])
- {
- gmx_fatal_collective(FARGS,NULL,dd,
- "The box size in direction %c (%f) times the triclinic skew factor (%f) is too small for a cut-off of %f with %d domain decomposition cells, use 1 or more than %d %s or increase the box size in this direction",
- dim2char(d),ddbox->box_size[d],ddbox->skew_fac[d],
- comm->cutoff,
- dd->nc[d],dd->nc[d],
- dd->nnodes > dd->nc[d] ? "cells" : "processors");
- }
- }
-
- if (!comm->bDynLoadBal)
- {
- copy_rvec(cellsize_min,comm->cellsize_min);
- }
-
- for(d=0; d<comm->npmedecompdim; d++)
- {
- set_pme_maxshift(dd,&comm->ddpme[d],
- comm->slb_frac[dd->dim[d]]==NULL,ddbox,
- comm->ddpme[d].slb_dim_f);
- }
-}
-
-
-static void dd_cell_sizes_dlb_root_enforce_limits(gmx_domdec_t *dd,
- int d,int dim,gmx_domdec_root_t *root,
- gmx_ddbox_t *ddbox,
- gmx_bool bUniform,gmx_large_int_t step, real cellsize_limit_f, int range[])
-{
- gmx_domdec_comm_t *comm;
- int ncd,i,j,nmin,nmin_old;
- gmx_bool bLimLo,bLimHi;
- real *cell_size;
- real fac,halfway,cellsize_limit_f_i,region_size;
- gmx_bool bPBC,bLastHi=FALSE;
- int nrange[]={range[0],range[1]};
-
- region_size= root->cell_f[range[1]]-root->cell_f[range[0]];
-
- comm = dd->comm;
-
- ncd = dd->nc[dim];
-
- bPBC = (dim < ddbox->npbcdim);
-
- cell_size = root->buf_ncd;
-
- if (debug)
- {
- fprintf(debug,"enforce_limits: %d %d\n",range[0],range[1]);
- }
-
- /* First we need to check if the scaling does not make cells
- * smaller than the smallest allowed size.
- * We need to do this iteratively, since if a cell is too small,
- * it needs to be enlarged, which makes all the other cells smaller,
- * which could in turn make another cell smaller than allowed.
- */
- for(i=range[0]; i<range[1]; i++)
- {
- root->bCellMin[i] = FALSE;
- }
- nmin = 0;
- do
- {
- nmin_old = nmin;
- /* We need the total for normalization */
- fac = 0;
- for(i=range[0]; i<range[1]; i++)
- {
- if (root->bCellMin[i] == FALSE)
- {
- fac += cell_size[i];
- }
- }
- fac = ( region_size - nmin*cellsize_limit_f)/fac; /* substracting cells already set to cellsize_limit_f */
- /* Determine the cell boundaries */
- for(i=range[0]; i<range[1]; i++)
- {
- if (root->bCellMin[i] == FALSE)
- {
- cell_size[i] *= fac;
- if (!bPBC && (i == 0 || i == dd->nc[dim] -1))
- {
- cellsize_limit_f_i = 0;
- }
- else
- {
- cellsize_limit_f_i = cellsize_limit_f;
- }
- if (cell_size[i] < cellsize_limit_f_i)
- {
- root->bCellMin[i] = TRUE;
- cell_size[i] = cellsize_limit_f_i;
- nmin++;
- }
- }
- root->cell_f[i+1] = root->cell_f[i] + cell_size[i];
- }
- }
- while (nmin > nmin_old);
-
- i=range[1]-1;
- cell_size[i] = root->cell_f[i+1] - root->cell_f[i];
- /* For this check we should not use DD_CELL_MARGIN,
- * but a slightly smaller factor,
- * since rounding could get use below the limit.
- */
- if (bPBC && cell_size[i] < cellsize_limit_f*DD_CELL_MARGIN2/DD_CELL_MARGIN)
- {
- char buf[22];
- gmx_fatal(FARGS,"Step %s: the dynamic load balancing could not balance dimension %c: box size %f, triclinic skew factor %f, #cells %d, minimum cell size %f\n",
- gmx_step_str(step,buf),
- dim2char(dim),ddbox->box_size[dim],ddbox->skew_fac[dim],
- ncd,comm->cellsize_min[dim]);
- }
-
- root->bLimited = (nmin > 0) || (range[0]>0) || (range[1]<ncd);
-
- if (!bUniform)
- {
- /* Check if the boundary did not displace more than halfway
- * each of the cells it bounds, as this could cause problems,
- * especially when the differences between cell sizes are large.
- * If changes are applied, they will not make cells smaller
- * than the cut-off, as we check all the boundaries which
- * might be affected by a change and if the old state was ok,
- * the cells will at most be shrunk back to their old size.
- */
- for(i=range[0]+1; i<range[1]; i++)
- {
- halfway = 0.5*(root->old_cell_f[i] + root->old_cell_f[i-1]);
- if (root->cell_f[i] < halfway)
- {
- root->cell_f[i] = halfway;
- /* Check if the change also causes shifts of the next boundaries */
- for(j=i+1; j<range[1]; j++)
- {
- if (root->cell_f[j] < root->cell_f[j-1] + cellsize_limit_f)
- root->cell_f[j] = root->cell_f[j-1] + cellsize_limit_f;
- }
- }
- halfway = 0.5*(root->old_cell_f[i] + root->old_cell_f[i+1]);
- if (root->cell_f[i] > halfway)
- {
- root->cell_f[i] = halfway;
- /* Check if the change also causes shifts of the next boundaries */
- for(j=i-1; j>=range[0]+1; j--)
- {
- if (root->cell_f[j] > root->cell_f[j+1] - cellsize_limit_f)
- root->cell_f[j] = root->cell_f[j+1] - cellsize_limit_f;
- }
- }
- }
- }
-
- /* nrange is defined as [lower, upper) range for new call to enforce_limits */
- /* find highest violation of LimLo (a) and the following violation of LimHi (thus the lowest following) (b)
- * then call enforce_limits for (oldb,a), (a,b). In the next step: (b,nexta). oldb and nexta can be the boundaries.
- * for a and b nrange is used */
- if (d > 0)
- {
- /* Take care of the staggering of the cell boundaries */
- if (bUniform)
- {
- for(i=range[0]; i<range[1]; i++)
- {
- root->cell_f_max0[i] = root->cell_f[i];
- root->cell_f_min1[i] = root->cell_f[i+1];
- }
- }
- else
- {
- for(i=range[0]+1; i<range[1]; i++)
- {
- bLimLo = (root->cell_f[i] < root->bound_min[i]);
- bLimHi = (root->cell_f[i] > root->bound_max[i]);
- if (bLimLo && bLimHi)
- {
- /* Both limits violated, try the best we can */
- /* For this case we split the original range (range) in two parts and care about the other limitiations in the next iteration. */
- root->cell_f[i] = 0.5*(root->bound_min[i] + root->bound_max[i]);
- nrange[0]=range[0];
- nrange[1]=i;
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
-
- nrange[0]=i;
- nrange[1]=range[1];
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
-
- return;
- }
- else if (bLimLo)
- {
- /* root->cell_f[i] = root->bound_min[i]; */
- nrange[1]=i; /* only store violation location. There could be a LimLo violation following with an higher index */
- bLastHi=FALSE;
- }
- else if (bLimHi && !bLastHi)
- {
- bLastHi=TRUE;
- if (nrange[1] < range[1]) /* found a LimLo before */
- {
- root->cell_f[nrange[1]] = root->bound_min[nrange[1]];
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
- nrange[0]=nrange[1];
- }
- root->cell_f[i] = root->bound_max[i];
- nrange[1]=i;
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
- nrange[0]=i;
- nrange[1]=range[1];
- }
- }
- if (nrange[1] < range[1]) /* found last a LimLo */
- {
- root->cell_f[nrange[1]] = root->bound_min[nrange[1]];
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
- nrange[0]=nrange[1];
- nrange[1]=range[1];
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
- }
- else if (nrange[0] > range[0]) /* found at least one LimHi */
- {
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
- }
- }
- }
-}
-
-
-static void set_dd_cell_sizes_dlb_root(gmx_domdec_t *dd,
- int d,int dim,gmx_domdec_root_t *root,
- gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
- gmx_bool bUniform,gmx_large_int_t step)
-{
- gmx_domdec_comm_t *comm;
- int ncd,d1,i,j,pos;
- real *cell_size;
- real load_aver,load_i,imbalance,change,change_max,sc;
- real cellsize_limit_f,dist_min_f,dist_min_f_hard,space;
- real change_limit = 0.1;
- real relax = 0.5;
- gmx_bool bPBC;
- int range[] = { 0, 0 };
-
- comm = dd->comm;
-
- ncd = dd->nc[dim];
-
- bPBC = (dim < ddbox->npbcdim);
-
- cell_size = root->buf_ncd;
-
- /* Store the original boundaries */
- for(i=0; i<ncd+1; i++)
- {
- root->old_cell_f[i] = root->cell_f[i];
- }
- if (bUniform) {
- for(i=0; i<ncd; i++)
- {
- cell_size[i] = 1.0/ncd;
- }
- }
- else if (dd_load_count(comm))
- {
- load_aver = comm->load[d].sum_m/ncd;
- change_max = 0;
- for(i=0; i<ncd; i++)
- {
- /* Determine the relative imbalance of cell i */
- load_i = comm->load[d].load[i*comm->load[d].nload+2];
- imbalance = (load_i - load_aver)/(load_aver>0 ? load_aver : 1);
- /* Determine the change of the cell size using underrelaxation */
- change = -relax*imbalance;
- change_max = max(change_max,max(change,-change));
- }
- /* Limit the amount of scaling.
- * We need to use the same rescaling for all cells in one row,
- * otherwise the load balancing might not converge.
- */
- sc = relax;
- if (change_max > change_limit)
- {
- sc *= change_limit/change_max;
- }
- for(i=0; i<ncd; i++)
- {
- /* Determine the relative imbalance of cell i */
- load_i = comm->load[d].load[i*comm->load[d].nload+2];
- imbalance = (load_i - load_aver)/(load_aver>0 ? load_aver : 1);
- /* Determine the change of the cell size using underrelaxation */
- change = -sc*imbalance;
- cell_size[i] = (root->cell_f[i+1]-root->cell_f[i])*(1 + change);
- }
- }
-
- cellsize_limit_f = comm->cellsize_min[dim]/ddbox->box_size[dim];
- cellsize_limit_f *= DD_CELL_MARGIN;
- dist_min_f_hard = grid_jump_limit(comm,d)/ddbox->box_size[dim];
- dist_min_f = dist_min_f_hard * DD_CELL_MARGIN;
- if (ddbox->tric_dir[dim])
- {
- cellsize_limit_f /= ddbox->skew_fac[dim];
- dist_min_f /= ddbox->skew_fac[dim];
- }
- if (bDynamicBox && d > 0)
- {
- dist_min_f *= DD_PRES_SCALE_MARGIN;
- }
- if (d > 0 && !bUniform)
- {
- /* Make sure that the grid is not shifted too much */
- for(i=1; i<ncd; i++) {
- if (root->cell_f_min1[i] - root->cell_f_max0[i-1] < 2 * dist_min_f_hard)
- {
- gmx_incons("Inconsistent DD boundary staggering limits!");
- }
- root->bound_min[i] = root->cell_f_max0[i-1] + dist_min_f;
- space = root->cell_f[i] - (root->cell_f_max0[i-1] + dist_min_f);
- if (space > 0) {
- root->bound_min[i] += 0.5*space;
- }
- root->bound_max[i] = root->cell_f_min1[i] - dist_min_f;
- space = root->cell_f[i] - (root->cell_f_min1[i] - dist_min_f);
- if (space < 0) {
- root->bound_max[i] += 0.5*space;
- }
- if (debug)
- {
- fprintf(debug,
- "dim %d boundary %d %.3f < %.3f < %.3f < %.3f < %.3f\n",
- d,i,
- root->cell_f_max0[i-1] + dist_min_f,
- root->bound_min[i],root->cell_f[i],root->bound_max[i],
- root->cell_f_min1[i] - dist_min_f);
- }
- }
- }
- range[1]=ncd;
- root->cell_f[0] = 0;
- root->cell_f[ncd] = 1;
- dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, range);
-
-
- /* After the checks above, the cells should obey the cut-off
- * restrictions, but it does not hurt to check.
- */
- for(i=0; i<ncd; i++)
- {
- if (debug)
- {
- fprintf(debug,"Relative bounds dim %d cell %d: %f %f\n",
- dim,i,root->cell_f[i],root->cell_f[i+1]);
- }
-
- if ((bPBC || (i != 0 && i != dd->nc[dim]-1)) &&
- root->cell_f[i+1] - root->cell_f[i] <
- cellsize_limit_f/DD_CELL_MARGIN)
- {
- char buf[22];
- fprintf(stderr,
- "\nWARNING step %s: direction %c, cell %d too small: %f\n",
- gmx_step_str(step,buf),dim2char(dim),i,
- (root->cell_f[i+1] - root->cell_f[i])
- *ddbox->box_size[dim]*ddbox->skew_fac[dim]);
- }
- }
-
- pos = ncd + 1;
- /* Store the cell boundaries of the lower dimensions at the end */
- for(d1=0; d1<d; d1++)
- {
- root->cell_f[pos++] = comm->cell_f0[d1];
- root->cell_f[pos++] = comm->cell_f1[d1];
- }
-
- if (d < comm->npmedecompdim)
- {
- /* The master determines the maximum shift for
- * the coordinate communication between separate PME nodes.
- */
- set_pme_maxshift(dd,&comm->ddpme[d],bUniform,ddbox,root->cell_f);
- }
- root->cell_f[pos++] = comm->ddpme[0].maxshift;
- if (d >= 1)
- {
- root->cell_f[pos++] = comm->ddpme[1].maxshift;
- }
-}
-
-static void relative_to_absolute_cell_bounds(gmx_domdec_t *dd,
- gmx_ddbox_t *ddbox,int dimind)
-{
- gmx_domdec_comm_t *comm;
- int dim;
-
- comm = dd->comm;
-
- /* Set the cell dimensions */
- dim = dd->dim[dimind];
- comm->cell_x0[dim] = comm->cell_f0[dimind]*ddbox->box_size[dim];
- comm->cell_x1[dim] = comm->cell_f1[dimind]*ddbox->box_size[dim];
- if (dim >= ddbox->nboundeddim)
- {
- comm->cell_x0[dim] += ddbox->box0[dim];
- comm->cell_x1[dim] += ddbox->box0[dim];
- }
-}
-
-static void distribute_dd_cell_sizes_dlb(gmx_domdec_t *dd,
- int d,int dim,real *cell_f_row,
- gmx_ddbox_t *ddbox)
-{
- gmx_domdec_comm_t *comm;
- int d1,dim1,pos;
-
- comm = dd->comm;
-
-#ifdef GMX_MPI
- /* Each node would only need to know two fractions,
- * but it is probably cheaper to broadcast the whole array.
- */
- MPI_Bcast(cell_f_row,DD_CELL_F_SIZE(dd,d)*sizeof(real),MPI_BYTE,
- 0,comm->mpi_comm_load[d]);
-#endif
- /* Copy the fractions for this dimension from the buffer */
- comm->cell_f0[d] = cell_f_row[dd->ci[dim] ];
- comm->cell_f1[d] = cell_f_row[dd->ci[dim]+1];
- /* The whole array was communicated, so set the buffer position */
- pos = dd->nc[dim] + 1;
- for(d1=0; d1<=d; d1++)
- {
- if (d1 < d)
- {
- /* Copy the cell fractions of the lower dimensions */
- comm->cell_f0[d1] = cell_f_row[pos++];
- comm->cell_f1[d1] = cell_f_row[pos++];
- }
- relative_to_absolute_cell_bounds(dd,ddbox,d1);
- }
- /* Convert the communicated shift from float to int */
- comm->ddpme[0].maxshift = (int)(cell_f_row[pos++] + 0.5);
- if (d >= 1)
- {
- comm->ddpme[1].maxshift = (int)(cell_f_row[pos++] + 0.5);
- }
-}
-
-static void set_dd_cell_sizes_dlb_change(gmx_domdec_t *dd,
- gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
- gmx_bool bUniform,gmx_large_int_t step)
-{
- gmx_domdec_comm_t *comm;
- int d,dim,d1;
- gmx_bool bRowMember,bRowRoot;
- real *cell_f_row;
-
- comm = dd->comm;
-
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- bRowMember = TRUE;
- bRowRoot = TRUE;
- for(d1=d; d1<dd->ndim; d1++)
- {
- if (dd->ci[dd->dim[d1]] > 0)
- {
- if (d1 > d)
- {
- bRowMember = FALSE;
- }
- bRowRoot = FALSE;
- }
- }
- if (bRowMember)
- {
- if (bRowRoot)
- {
- set_dd_cell_sizes_dlb_root(dd,d,dim,comm->root[d],
- ddbox,bDynamicBox,bUniform,step);
- cell_f_row = comm->root[d]->cell_f;
- }
- else
- {
- cell_f_row = comm->cell_f_row;
- }
- distribute_dd_cell_sizes_dlb(dd,d,dim,cell_f_row,ddbox);
- }
- }
-}
-
-static void set_dd_cell_sizes_dlb_nochange(gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
-{
- int d;
-
- /* This function assumes the box is static and should therefore
- * not be called when the box has changed since the last
- * call to dd_partition_system.
- */
- for(d=0; d<dd->ndim; d++)
- {
- relative_to_absolute_cell_bounds(dd,ddbox,d);
- }
-}
-
-
-
-static void set_dd_cell_sizes_dlb(gmx_domdec_t *dd,
- gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
- gmx_bool bUniform,gmx_bool bDoDLB,gmx_large_int_t step,
- gmx_wallcycle_t wcycle)
-{
- gmx_domdec_comm_t *comm;
- int dim;
-
- comm = dd->comm;
-
- if (bDoDLB)
- {
- wallcycle_start(wcycle,ewcDDCOMMBOUND);
- set_dd_cell_sizes_dlb_change(dd,ddbox,bDynamicBox,bUniform,step);
- wallcycle_stop(wcycle,ewcDDCOMMBOUND);
- }
- else if (bDynamicBox)
- {
- set_dd_cell_sizes_dlb_nochange(dd,ddbox);
- }
-
- /* Set the dimensions for which no DD is used */
- for(dim=0; dim<DIM; dim++) {
- if (dd->nc[dim] == 1) {
- comm->cell_x0[dim] = 0;
- comm->cell_x1[dim] = ddbox->box_size[dim];
- if (dim >= ddbox->nboundeddim)
- {
- comm->cell_x0[dim] += ddbox->box0[dim];
- comm->cell_x1[dim] += ddbox->box0[dim];
- }
- }
- }
-}
-
-static void realloc_comm_ind(gmx_domdec_t *dd,ivec npulse)
-{
- int d,np,i;
- gmx_domdec_comm_dim_t *cd;
-
- for(d=0; d<dd->ndim; d++)
- {
- cd = &dd->comm->cd[d];
- np = npulse[dd->dim[d]];
- if (np > cd->np_nalloc)
- {
- if (debug)
- {
- fprintf(debug,"(Re)allocing cd for %c to %d pulses\n",
- dim2char(dd->dim[d]),np);
- }
- if (DDMASTER(dd) && cd->np_nalloc > 0)
- {
- fprintf(stderr,"\nIncreasing the number of cell to communicate in dimension %c to %d for the first time\n",dim2char(dd->dim[d]),np);
- }
- srenew(cd->ind,np);
- for(i=cd->np_nalloc; i<np; i++)
- {
- cd->ind[i].index = NULL;
- cd->ind[i].nalloc = 0;
- }
- cd->np_nalloc = np;
- }
- cd->np = np;
- }
-}
-
-
-static void set_dd_cell_sizes(gmx_domdec_t *dd,
- gmx_ddbox_t *ddbox,gmx_bool bDynamicBox,
- gmx_bool bUniform,gmx_bool bDoDLB,gmx_large_int_t step,
- gmx_wallcycle_t wcycle)
-{
- gmx_domdec_comm_t *comm;
- int d;
- ivec npulse;
-
- comm = dd->comm;
-
- /* Copy the old cell boundaries for the cg displacement check */
- copy_rvec(comm->cell_x0,comm->old_cell_x0);
- copy_rvec(comm->cell_x1,comm->old_cell_x1);
-
- if (comm->bDynLoadBal)
- {
- if (DDMASTER(dd))
- {
- check_box_size(dd,ddbox);
- }
- set_dd_cell_sizes_dlb(dd,ddbox,bDynamicBox,bUniform,bDoDLB,step,wcycle);
- }
- else
- {
- set_dd_cell_sizes_slb(dd,ddbox,FALSE,npulse);
- realloc_comm_ind(dd,npulse);
- }
-
- if (debug)
- {
- for(d=0; d<DIM; d++)
- {
- fprintf(debug,"cell_x[%d] %f - %f skew_fac %f\n",
- d,comm->cell_x0[d],comm->cell_x1[d],ddbox->skew_fac[d]);
- }
- }
-}
-
-static void comm_dd_ns_cell_sizes(gmx_domdec_t *dd,
- gmx_ddbox_t *ddbox,
- rvec cell_ns_x0,rvec cell_ns_x1,
- gmx_large_int_t step)
-{
- gmx_domdec_comm_t *comm;
- int dim_ind,dim;
-
- comm = dd->comm;
-
- for(dim_ind=0; dim_ind<dd->ndim; dim_ind++)
- {
- dim = dd->dim[dim_ind];
-
- /* Without PBC we don't have restrictions on the outer cells */
- if (!(dim >= ddbox->npbcdim &&
- (dd->ci[dim] == 0 || dd->ci[dim] == dd->nc[dim] - 1)) &&
- comm->bDynLoadBal &&
- (comm->cell_x1[dim] - comm->cell_x0[dim])*ddbox->skew_fac[dim] <
- comm->cellsize_min[dim])
- {
- char buf[22];
- gmx_fatal(FARGS,"Step %s: The %c-size (%f) times the triclinic skew factor (%f) is smaller than the smallest allowed cell size (%f) for domain decomposition grid cell %d %d %d",
- gmx_step_str(step,buf),dim2char(dim),
- comm->cell_x1[dim] - comm->cell_x0[dim],
- ddbox->skew_fac[dim],
- dd->comm->cellsize_min[dim],
- dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
- }
- }
-
- if ((dd->bGridJump && dd->ndim > 1) || ddbox->nboundeddim < DIM)
- {
- /* Communicate the boundaries and update cell_ns_x0/1 */
- dd_move_cellx(dd,ddbox,cell_ns_x0,cell_ns_x1);
- if (dd->bGridJump && dd->ndim > 1)
- {
- check_grid_jump(step,dd,ddbox);
- }
- }
-}
-
-static void make_tric_corr_matrix(int npbcdim,matrix box,matrix tcm)
-{
- if (YY < npbcdim)
- {
- tcm[YY][XX] = -box[YY][XX]/box[YY][YY];
- }
- else
- {
- tcm[YY][XX] = 0;
- }
- if (ZZ < npbcdim)
- {
- tcm[ZZ][XX] = -(box[ZZ][YY]*tcm[YY][XX] + box[ZZ][XX])/box[ZZ][ZZ];
- tcm[ZZ][YY] = -box[ZZ][YY]/box[ZZ][ZZ];
- }
- else
- {
- tcm[ZZ][XX] = 0;
- tcm[ZZ][YY] = 0;
- }
-}
-
-static void check_screw_box(matrix box)
-{
- /* Mathematical limitation */
- if (box[YY][XX] != 0 || box[ZZ][XX] != 0)
- {
- gmx_fatal(FARGS,"With screw pbc the unit cell can not have non-zero off-diagonal x-components");
- }
-
- /* Limitation due to the asymmetry of the eighth shell method */
- if (box[ZZ][YY] != 0)
- {
- gmx_fatal(FARGS,"pbc=screw with non-zero box_zy is not supported");
- }
-}
-
-static void distribute_cg(FILE *fplog,gmx_large_int_t step,
- matrix box,ivec tric_dir,t_block *cgs,rvec pos[],
- gmx_domdec_t *dd)
-{
- gmx_domdec_master_t *ma;
- int **tmp_ind=NULL,*tmp_nalloc=NULL;
- int i,icg,j,k,k0,k1,d,npbcdim;
- matrix tcm;
- rvec box_size,cg_cm;
- ivec ind;
- real nrcg,inv_ncg,pos_d;
- atom_id *cgindex;
- gmx_bool bUnbounded,bScrew;
-
- ma = dd->ma;
-
- if (tmp_ind == NULL)
- {
- snew(tmp_nalloc,dd->nnodes);
- snew(tmp_ind,dd->nnodes);
- for(i=0; i<dd->nnodes; i++)
- {
- tmp_nalloc[i] = over_alloc_large(cgs->nr/dd->nnodes+1);
- snew(tmp_ind[i],tmp_nalloc[i]);
- }
- }
-
- /* Clear the count */
- for(i=0; i<dd->nnodes; i++)
- {
- ma->ncg[i] = 0;
- ma->nat[i] = 0;
- }
-
- make_tric_corr_matrix(dd->npbcdim,box,tcm);
-
- cgindex = cgs->index;
-
- /* Compute the center of geometry for all charge groups */
- for(icg=0; icg<cgs->nr; icg++)
- {
- k0 = cgindex[icg];
- k1 = cgindex[icg+1];
- nrcg = k1 - k0;
- if (nrcg == 1)
- {
- copy_rvec(pos[k0],cg_cm);
- }
- else
- {
- inv_ncg = 1.0/nrcg;
-
- clear_rvec(cg_cm);
- for(k=k0; (k<k1); k++)
- {
- rvec_inc(cg_cm,pos[k]);
- }
- for(d=0; (d<DIM); d++)
- {
- cg_cm[d] *= inv_ncg;
- }
- }
- /* Put the charge group in the box and determine the cell index */
- for(d=DIM-1; d>=0; d--) {
- pos_d = cg_cm[d];
- if (d < dd->npbcdim)
- {
- bScrew = (dd->bScrewPBC && d == XX);
- if (tric_dir[d] && dd->nc[d] > 1)
- {
- /* Use triclinic coordintates for this dimension */
- for(j=d+1; j<DIM; j++)
- {
- pos_d += cg_cm[j]*tcm[j][d];
- }
- }
- while(pos_d >= box[d][d])
- {
- pos_d -= box[d][d];
- rvec_dec(cg_cm,box[d]);
- if (bScrew)
- {
- cg_cm[YY] = box[YY][YY] - cg_cm[YY];
- cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
- }
- for(k=k0; (k<k1); k++)
- {
- rvec_dec(pos[k],box[d]);
- if (bScrew)
- {
- pos[k][YY] = box[YY][YY] - pos[k][YY];
- pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
- }
- }
- }
- while(pos_d < 0)
- {
- pos_d += box[d][d];
- rvec_inc(cg_cm,box[d]);
- if (bScrew)
- {
- cg_cm[YY] = box[YY][YY] - cg_cm[YY];
- cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
- }
- for(k=k0; (k<k1); k++)
- {
- rvec_inc(pos[k],box[d]);
- if (bScrew) {
- pos[k][YY] = box[YY][YY] - pos[k][YY];
- pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
- }
- }
- }
- }
- /* This could be done more efficiently */
- ind[d] = 0;
- while(ind[d]+1 < dd->nc[d] && pos_d >= ma->cell_x[d][ind[d]+1])
- {
- ind[d]++;
- }
- }
- i = dd_index(dd->nc,ind);
- if (ma->ncg[i] == tmp_nalloc[i])
- {
- tmp_nalloc[i] = over_alloc_large(ma->ncg[i]+1);
- srenew(tmp_ind[i],tmp_nalloc[i]);
- }
- tmp_ind[i][ma->ncg[i]] = icg;
- ma->ncg[i]++;
- ma->nat[i] += cgindex[icg+1] - cgindex[icg];
- }
-
- k1 = 0;
- for(i=0; i<dd->nnodes; i++)
- {
- ma->index[i] = k1;
- for(k=0; k<ma->ncg[i]; k++)
- {
- ma->cg[k1++] = tmp_ind[i][k];
- }
- }
- ma->index[dd->nnodes] = k1;
-
- for(i=0; i<dd->nnodes; i++)
- {
- sfree(tmp_ind[i]);
- }
- sfree(tmp_ind);
- sfree(tmp_nalloc);
-
- if (fplog)
- {
- char buf[22];
- fprintf(fplog,"Charge group distribution at step %s:",
- gmx_step_str(step,buf));
- for(i=0; i<dd->nnodes; i++)
- {
- fprintf(fplog," %d",ma->ncg[i]);
- }
- fprintf(fplog,"\n");
- }
-}
-
-static void get_cg_distribution(FILE *fplog,gmx_large_int_t step,gmx_domdec_t *dd,
- t_block *cgs,matrix box,gmx_ddbox_t *ddbox,
- rvec pos[])
-{
- gmx_domdec_master_t *ma=NULL;
- ivec npulse;
- int i,cg_gl;
- int *ibuf,buf2[2] = { 0, 0 };
-
- if (DDMASTER(dd))
- {
- ma = dd->ma;
-
- if (dd->bScrewPBC)
- {
- check_screw_box(box);
- }
-
- set_dd_cell_sizes_slb(dd,ddbox,TRUE,npulse);
-
- distribute_cg(fplog,step,box,ddbox->tric_dir,cgs,pos,dd);
- for(i=0; i<dd->nnodes; i++)
- {
- ma->ibuf[2*i] = ma->ncg[i];
- ma->ibuf[2*i+1] = ma->nat[i];
- }
- ibuf = ma->ibuf;
- }
- else
- {
- ibuf = NULL;
- }
- dd_scatter(dd,2*sizeof(int),ibuf,buf2);
-
- dd->ncg_home = buf2[0];
- dd->nat_home = buf2[1];
- dd->ncg_tot = dd->ncg_home;
- dd->nat_tot = dd->nat_home;
- if (dd->ncg_home > dd->cg_nalloc || dd->cg_nalloc == 0)
- {
- dd->cg_nalloc = over_alloc_dd(dd->ncg_home);
- srenew(dd->index_gl,dd->cg_nalloc);
- srenew(dd->cgindex,dd->cg_nalloc+1);
- }
- if (DDMASTER(dd))
- {
- for(i=0; i<dd->nnodes; i++)
- {
- ma->ibuf[i] = ma->ncg[i]*sizeof(int);
- ma->ibuf[dd->nnodes+i] = ma->index[i]*sizeof(int);
- }
- }
-
- dd_scatterv(dd,
- DDMASTER(dd) ? ma->ibuf : NULL,
- DDMASTER(dd) ? ma->ibuf+dd->nnodes : NULL,
- DDMASTER(dd) ? ma->cg : NULL,
- dd->ncg_home*sizeof(int),dd->index_gl);
-
- /* Determine the home charge group sizes */
- dd->cgindex[0] = 0;
- for(i=0; i<dd->ncg_home; i++)
- {
- cg_gl = dd->index_gl[i];
- dd->cgindex[i+1] =
- dd->cgindex[i] + cgs->index[cg_gl+1] - cgs->index[cg_gl];
- }
-
- if (debug)
- {
- fprintf(debug,"Home charge groups:\n");
- for(i=0; i<dd->ncg_home; i++)
- {
- fprintf(debug," %d",dd->index_gl[i]);
- if (i % 10 == 9)
- fprintf(debug,"\n");
- }
- fprintf(debug,"\n");
- }
-}
-
-static int compact_and_copy_vec_at(int ncg,int *move,
- int *cgindex,
- int nvec,int vec,
- rvec *src,gmx_domdec_comm_t *comm,
- gmx_bool bCompact)
-{
- int m,icg,i,i0,i1,nrcg;
- int home_pos;
- int pos_vec[DIM*2];
-
- home_pos = 0;
-
- for(m=0; m<DIM*2; m++)
- {
- pos_vec[m] = 0;
- }
-
- i0 = 0;
- for(icg=0; icg<ncg; icg++)
- {
- i1 = cgindex[icg+1];
- m = move[icg];
- if (m == -1)
- {
- if (bCompact)
- {
- /* Compact the home array in place */
- for(i=i0; i<i1; i++)
- {
- copy_rvec(src[i],src[home_pos++]);
- }
- }
- }
- else
- {
- /* Copy to the communication buffer */
- nrcg = i1 - i0;
- pos_vec[m] += 1 + vec*nrcg;
- for(i=i0; i<i1; i++)
- {
- copy_rvec(src[i],comm->cgcm_state[m][pos_vec[m]++]);
- }
- pos_vec[m] += (nvec - vec - 1)*nrcg;
- }
- if (!bCompact)
- {
- home_pos += i1 - i0;
- }
- i0 = i1;
- }
-
- return home_pos;
-}
-
-static int compact_and_copy_vec_cg(int ncg,int *move,
- int *cgindex,
- int nvec,rvec *src,gmx_domdec_comm_t *comm,
- gmx_bool bCompact)
-{
- int m,icg,i0,i1,nrcg;
- int home_pos;
- int pos_vec[DIM*2];
-
- home_pos = 0;
-
- for(m=0; m<DIM*2; m++)
- {
- pos_vec[m] = 0;
- }
-
- i0 = 0;
- for(icg=0; icg<ncg; icg++)
- {
- i1 = cgindex[icg+1];
- m = move[icg];
- if (m == -1)
- {
- if (bCompact)
- {
- /* Compact the home array in place */
- copy_rvec(src[icg],src[home_pos++]);
- }
- }
- else
- {
- nrcg = i1 - i0;
- /* Copy to the communication buffer */
- copy_rvec(src[icg],comm->cgcm_state[m][pos_vec[m]]);
- pos_vec[m] += 1 + nrcg*nvec;
- }
- i0 = i1;
- }
- if (!bCompact)
- {
- home_pos = ncg;
- }
-
- return home_pos;
-}
-
-static int compact_ind(int ncg,int *move,
- int *index_gl,int *cgindex,
- int *gatindex,
- gmx_ga2la_t ga2la,char *bLocalCG,
- int *cginfo)
-{
- int cg,nat,a0,a1,a,a_gl;
- int home_pos;
-
- home_pos = 0;
- nat = 0;
- for(cg=0; cg<ncg; cg++)
- {
- a0 = cgindex[cg];
- a1 = cgindex[cg+1];
- if (move[cg] == -1)
- {
- /* Compact the home arrays in place.
- * Anything that can be done here avoids access to global arrays.
- */
- cgindex[home_pos] = nat;
- for(a=a0; a<a1; a++)
- {
- a_gl = gatindex[a];
- gatindex[nat] = a_gl;
- /* The cell number stays 0, so we don't need to set it */
- ga2la_change_la(ga2la,a_gl,nat);
- nat++;
- }
- index_gl[home_pos] = index_gl[cg];
- cginfo[home_pos] = cginfo[cg];
- /* The charge group remains local, so bLocalCG does not change */
- home_pos++;
- }
- else
- {
- /* Clear the global indices */
- for(a=a0; a<a1; a++)
- {
- ga2la_del(ga2la,gatindex[a]);
- }
- if (bLocalCG)
- {
- bLocalCG[index_gl[cg]] = FALSE;
- }
- }
- }
- cgindex[home_pos] = nat;
-
- return home_pos;
-}
-
-static void clear_and_mark_ind(int ncg,int *move,
- int *index_gl,int *cgindex,int *gatindex,
- gmx_ga2la_t ga2la,char *bLocalCG,
- int *cell_index)
-{
- int cg,a0,a1,a;
-
- for(cg=0; cg<ncg; cg++)
- {
- if (move[cg] >= 0)
- {
- a0 = cgindex[cg];
- a1 = cgindex[cg+1];
- /* Clear the global indices */
- for(a=a0; a<a1; a++)
- {
- ga2la_del(ga2la,gatindex[a]);
- }
- if (bLocalCG)
- {
- bLocalCG[index_gl[cg]] = FALSE;
- }
- /* Signal that this cg has moved using the ns cell index.
- * Here we set it to -1.
- * fill_grid will change it from -1 to 4*grid->ncells.
- */
- cell_index[cg] = -1;
- }
- }
-}
-
-static void print_cg_move(FILE *fplog,
- gmx_domdec_t *dd,
- gmx_large_int_t step,int cg,int dim,int dir,
- gmx_bool bHaveLimitdAndCMOld,real limitd,
- rvec cm_old,rvec cm_new,real pos_d)
-{
- gmx_domdec_comm_t *comm;
- char buf[22];
-
- comm = dd->comm;
-
- fprintf(fplog,"\nStep %s:\n",gmx_step_str(step,buf));
- if (bHaveLimitdAndCMOld)
- {
- fprintf(fplog,"The charge group starting at atom %d moved than the distance allowed by the domain decomposition (%f) in direction %c\n",
- ddglatnr(dd,dd->cgindex[cg]),limitd,dim2char(dim));
- }
- else
- {
- fprintf(fplog,"The charge group starting at atom %d moved than the distance allowed by the domain decomposition in direction %c\n",
- ddglatnr(dd,dd->cgindex[cg]),dim2char(dim));
- }
- fprintf(fplog,"distance out of cell %f\n",
- dir==1 ? pos_d - comm->cell_x1[dim] : pos_d - comm->cell_x0[dim]);
- if (bHaveLimitdAndCMOld)
- {
- fprintf(fplog,"Old coordinates: %8.3f %8.3f %8.3f\n",
- cm_old[XX],cm_old[YY],cm_old[ZZ]);
- }
- fprintf(fplog,"New coordinates: %8.3f %8.3f %8.3f\n",
- cm_new[XX],cm_new[YY],cm_new[ZZ]);
- fprintf(fplog,"Old cell boundaries in direction %c: %8.3f %8.3f\n",
- dim2char(dim),
- comm->old_cell_x0[dim],comm->old_cell_x1[dim]);
- fprintf(fplog,"New cell boundaries in direction %c: %8.3f %8.3f\n",
- dim2char(dim),
- comm->cell_x0[dim],comm->cell_x1[dim]);
-}
-
-static void cg_move_error(FILE *fplog,
- gmx_domdec_t *dd,
- gmx_large_int_t step,int cg,int dim,int dir,
- gmx_bool bHaveLimitdAndCMOld,real limitd,
- rvec cm_old,rvec cm_new,real pos_d)
-{
- if (fplog)
- {
- print_cg_move(fplog, dd,step,cg,dim,dir,
- bHaveLimitdAndCMOld,limitd,cm_old,cm_new,pos_d);
- }
- print_cg_move(stderr,dd,step,cg,dim,dir,
- bHaveLimitdAndCMOld,limitd,cm_old,cm_new,pos_d);
- gmx_fatal(FARGS,
- "A charge group moved too far between two domain decomposition steps\n"
- "This usually means that your system is not well equilibrated");
-}
-
-static void rotate_state_atom(t_state *state,int a)
-{
- int est;
-
- for(est=0; est<estNR; est++)
- {
- if (EST_DISTR(est) && state->flags & (1<<est)) {
- switch (est) {
- case estX:
- /* Rotate the complete state; for a rectangular box only */
- state->x[a][YY] = state->box[YY][YY] - state->x[a][YY];
- state->x[a][ZZ] = state->box[ZZ][ZZ] - state->x[a][ZZ];
- break;
- case estV:
- state->v[a][YY] = -state->v[a][YY];
- state->v[a][ZZ] = -state->v[a][ZZ];
- break;
- case estSDX:
- state->sd_X[a][YY] = -state->sd_X[a][YY];
- state->sd_X[a][ZZ] = -state->sd_X[a][ZZ];
- break;
- case estCGP:
- state->cg_p[a][YY] = -state->cg_p[a][YY];
- state->cg_p[a][ZZ] = -state->cg_p[a][ZZ];
- break;
- case estDISRE_INITF:
- case estDISRE_RM3TAV:
- case estORIRE_INITF:
- case estORIRE_DTAV:
- /* These are distances, so not affected by rotation */
- break;
- default:
- gmx_incons("Unknown state entry encountered in rotate_state_atom");
- }
- }
- }
-}
-
-static int dd_redistribute_cg(FILE *fplog,gmx_large_int_t step,
- gmx_domdec_t *dd,ivec tric_dir,
- t_state *state,rvec **f,
- t_forcerec *fr,t_mdatoms *md,
- gmx_bool bCompact,
- t_nrnb *nrnb)
-{
- int *move;
- int npbcdim;
- int ncg[DIM*2],nat[DIM*2];
- int c,i,cg,k,k0,k1,d,dim,dim2,dir,d2,d3,d4,cell_d;
- int mc,cdd,nrcg,ncg_recv,nat_recv,nvs,nvr,nvec,vec;
- int sbuf[2],rbuf[2];
- int home_pos_cg,home_pos_at,ncg_stay_home,buf_pos;
- int flag;
- gmx_bool bV=FALSE,bSDX=FALSE,bCGP=FALSE;
- gmx_bool bScrew;
- ivec dev;
- real inv_ncg,pos_d;
- matrix tcm;
- rvec *cg_cm,cell_x0,cell_x1,limitd,limit0,limit1,cm_new;
- atom_id *cgindex;
- cginfo_mb_t *cginfo_mb;
- gmx_domdec_comm_t *comm;
-
- if (dd->bScrewPBC)
- {
- check_screw_box(state->box);
- }
-
- comm = dd->comm;
- cg_cm = fr->cg_cm;
-
- for(i=0; i<estNR; i++)
- {
- if (EST_DISTR(i))
- {
- switch (i)
- {
- case estX: /* Always present */ break;
- case estV: bV = (state->flags & (1<<i)); break;
- case estSDX: bSDX = (state->flags & (1<<i)); break;
- case estCGP: bCGP = (state->flags & (1<<i)); break;
- case estLD_RNG:
- case estLD_RNGI:
- case estDISRE_INITF:
- case estDISRE_RM3TAV:
- case estORIRE_INITF:
- case estORIRE_DTAV:
- /* No processing required */
- break;
- default:
- gmx_incons("Unknown state entry encountered in dd_redistribute_cg");
- }
- }
- }
-
- if (dd->ncg_tot > comm->nalloc_int)
- {
- comm->nalloc_int = over_alloc_dd(dd->ncg_tot);
- srenew(comm->buf_int,comm->nalloc_int);
- }
- move = comm->buf_int;
-
- /* Clear the count */
- for(c=0; c<dd->ndim*2; c++)
- {
- ncg[c] = 0;
- nat[c] = 0;
- }
-
- npbcdim = dd->npbcdim;
-
- for(d=0; (d<DIM); d++)
- {
- limitd[d] = dd->comm->cellsize_min[d];
- if (d >= npbcdim && dd->ci[d] == 0)
- {
- cell_x0[d] = -GMX_FLOAT_MAX;
- }
- else
- {
- cell_x0[d] = comm->cell_x0[d];
- }
- if (d >= npbcdim && dd->ci[d] == dd->nc[d] - 1)
- {
- cell_x1[d] = GMX_FLOAT_MAX;
- }
- else
- {
- cell_x1[d] = comm->cell_x1[d];
- }
- if (d < npbcdim)
- {
- limit0[d] = comm->old_cell_x0[d] - limitd[d];
- limit1[d] = comm->old_cell_x1[d] + limitd[d];
- }
- else
- {
- /* We check after communication if a charge group moved
- * more than one cell. Set the pre-comm check limit to float_max.
- */
- limit0[d] = -GMX_FLOAT_MAX;
- limit1[d] = GMX_FLOAT_MAX;
- }
- }
-
- make_tric_corr_matrix(npbcdim,state->box,tcm);
-
- cgindex = dd->cgindex;
-
- /* Compute the center of geometry for all home charge groups
- * and put them in the box and determine where they should go.
- */
- for(cg=0; cg<dd->ncg_home; cg++)
- {
- k0 = cgindex[cg];
- k1 = cgindex[cg+1];
- nrcg = k1 - k0;
- if (nrcg == 1)
- {
- copy_rvec(state->x[k0],cm_new);
- }
- else
- {
- inv_ncg = 1.0/nrcg;
-
- clear_rvec(cm_new);
- for(k=k0; (k<k1); k++)
- {
- rvec_inc(cm_new,state->x[k]);
- }
- for(d=0; (d<DIM); d++)
- {
- cm_new[d] = inv_ncg*cm_new[d];
- }
- }
-
- clear_ivec(dev);
- /* Do pbc and check DD cell boundary crossings */
- for(d=DIM-1; d>=0; d--)
- {
- if (dd->nc[d] > 1)
- {
- bScrew = (dd->bScrewPBC && d == XX);
- /* Determine the location of this cg in lattice coordinates */
- pos_d = cm_new[d];
- if (tric_dir[d])
- {
- for(d2=d+1; d2<DIM; d2++)
- {
- pos_d += cm_new[d2]*tcm[d2][d];
- }
- }
- /* Put the charge group in the triclinic unit-cell */
- if (pos_d >= cell_x1[d])
- {
- if (pos_d >= limit1[d])
- {
- cg_move_error(fplog,dd,step,cg,d,1,TRUE,limitd[d],
- cg_cm[cg],cm_new,pos_d);
- }
- dev[d] = 1;
- if (dd->ci[d] == dd->nc[d] - 1)
- {
- rvec_dec(cm_new,state->box[d]);
- if (bScrew)
- {
- cm_new[YY] = state->box[YY][YY] - cm_new[YY];
- cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
- }
- for(k=k0; (k<k1); k++)
- {
- rvec_dec(state->x[k],state->box[d]);
- if (bScrew)
- {
- rotate_state_atom(state,k);
- }
- }
- }
- }
- else if (pos_d < cell_x0[d])
- {
- if (pos_d < limit0[d])
- {
- cg_move_error(fplog,dd,step,cg,d,-1,TRUE,limitd[d],
- cg_cm[cg],cm_new,pos_d);
- }
- dev[d] = -1;
- if (dd->ci[d] == 0)
- {
- rvec_inc(cm_new,state->box[d]);
- if (bScrew)
- {
- cm_new[YY] = state->box[YY][YY] - cm_new[YY];
- cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
- }
- for(k=k0; (k<k1); k++)
- {
- rvec_inc(state->x[k],state->box[d]);
- if (bScrew)
- {
- rotate_state_atom(state,k);
- }
- }
- }
- }
- }
- else if (d < npbcdim)
- {
- /* Put the charge group in the rectangular unit-cell */
- while (cm_new[d] >= state->box[d][d])
- {
- rvec_dec(cm_new,state->box[d]);
- for(k=k0; (k<k1); k++)
- {
- rvec_dec(state->x[k],state->box[d]);
- }
- }
- while (cm_new[d] < 0)
- {
- rvec_inc(cm_new,state->box[d]);
- for(k=k0; (k<k1); k++)
- {
- rvec_inc(state->x[k],state->box[d]);
- }
- }
- }
- }
-
- copy_rvec(cm_new,cg_cm[cg]);
-
- /* Determine where this cg should go */
- flag = 0;
- mc = -1;
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- if (dev[dim] == 1)
- {
- flag |= DD_FLAG_FW(d);
- if (mc == -1)
- {
- mc = d*2;
- }
- }
- else if (dev[dim] == -1)
- {
- flag |= DD_FLAG_BW(d);
- if (mc == -1) {
- if (dd->nc[dim] > 2)
- {
- mc = d*2 + 1;
- }
- else
- {
- mc = d*2;
- }
- }
- }
- }
- move[cg] = mc;
- if (mc >= 0)
- {
- if (ncg[mc]+1 > comm->cggl_flag_nalloc[mc])
- {
- comm->cggl_flag_nalloc[mc] = over_alloc_dd(ncg[mc]+1);
- srenew(comm->cggl_flag[mc],comm->cggl_flag_nalloc[mc]*DD_CGIBS);
- }
- comm->cggl_flag[mc][ncg[mc]*DD_CGIBS ] = dd->index_gl[cg];
- /* We store the cg size in the lower 16 bits
- * and the place where the charge group should go
- * in the next 6 bits. This saves some communication volume.
- */
- comm->cggl_flag[mc][ncg[mc]*DD_CGIBS+1] = nrcg | flag;
- ncg[mc] += 1;
- nat[mc] += nrcg;
- }
- }
-
- inc_nrnb(nrnb,eNR_CGCM,dd->nat_home);
- inc_nrnb(nrnb,eNR_RESETX,dd->ncg_home);
-
- nvec = 1;
- if (bV)
- {
- nvec++;
- }
- if (bSDX)
- {
- nvec++;
- }
- if (bCGP)
- {
- nvec++;
- }
-
- /* Make sure the communication buffers are large enough */
- for(mc=0; mc<dd->ndim*2; mc++)
- {
- nvr = ncg[mc] + nat[mc]*nvec;
- if (nvr > comm->cgcm_state_nalloc[mc])
- {
- comm->cgcm_state_nalloc[mc] = over_alloc_dd(nvr);
- srenew(comm->cgcm_state[mc],comm->cgcm_state_nalloc[mc]);
- }
- }
-
- /* Recalculating cg_cm might be cheaper than communicating,
- * but that could give rise to rounding issues.
- */
- home_pos_cg =
- compact_and_copy_vec_cg(dd->ncg_home,move,cgindex,
- nvec,cg_cm,comm,bCompact);
-
- vec = 0;
- home_pos_at =
- compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
- nvec,vec++,state->x,comm,bCompact);
- if (bV)
- {
- compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
- nvec,vec++,state->v,comm,bCompact);
- }
- if (bSDX)
- {
- compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
- nvec,vec++,state->sd_X,comm,bCompact);
- }
- if (bCGP)
- {
- compact_and_copy_vec_at(dd->ncg_home,move,cgindex,
- nvec,vec++,state->cg_p,comm,bCompact);
- }
-
- if (bCompact)
- {
- compact_ind(dd->ncg_home,move,
- dd->index_gl,dd->cgindex,dd->gatindex,
- dd->ga2la,comm->bLocalCG,
- fr->cginfo);
- }
- else
- {
- clear_and_mark_ind(dd->ncg_home,move,
- dd->index_gl,dd->cgindex,dd->gatindex,
- dd->ga2la,comm->bLocalCG,
- fr->ns.grid->cell_index);
- }
-
- cginfo_mb = fr->cginfo_mb;
-
- ncg_stay_home = home_pos_cg;
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- ncg_recv = 0;
- nat_recv = 0;
- nvr = 0;
- for(dir=0; dir<(dd->nc[dim]==2 ? 1 : 2); dir++)
- {
- cdd = d*2 + dir;
- /* Communicate the cg and atom counts */
- sbuf[0] = ncg[cdd];
- sbuf[1] = nat[cdd];
- if (debug)
- {
- fprintf(debug,"Sending ddim %d dir %d: ncg %d nat %d\n",
- d,dir,sbuf[0],sbuf[1]);
- }
- dd_sendrecv_int(dd, d, dir, sbuf, 2, rbuf, 2);
-
- if ((ncg_recv+rbuf[0])*DD_CGIBS > comm->nalloc_int)
- {
- comm->nalloc_int = over_alloc_dd((ncg_recv+rbuf[0])*DD_CGIBS);
- srenew(comm->buf_int,comm->nalloc_int);
- }
-
- /* Communicate the charge group indices, sizes and flags */
- dd_sendrecv_int(dd, d, dir,
- comm->cggl_flag[cdd], sbuf[0]*DD_CGIBS,
- comm->buf_int+ncg_recv*DD_CGIBS, rbuf[0]*DD_CGIBS);
-
- nvs = ncg[cdd] + nat[cdd]*nvec;
- i = rbuf[0] + rbuf[1] *nvec;
- vec_rvec_check_alloc(&comm->vbuf,nvr+i);
-
- /* Communicate cgcm and state */
- dd_sendrecv_rvec(dd, d, dir,
- comm->cgcm_state[cdd], nvs,
- comm->vbuf.v+nvr, i);
- ncg_recv += rbuf[0];
- nat_recv += rbuf[1];
- nvr += i;
- }
-
- /* Process the received charge groups */
- buf_pos = 0;
- for(cg=0; cg<ncg_recv; cg++)
- {
- flag = comm->buf_int[cg*DD_CGIBS+1];
-
- if (dim >= npbcdim && dd->nc[dim] > 2)
- {
- /* No pbc in this dim and more than one domain boundary.
- * We to a separate check if a charge did not move too far.
- */
- if (((flag & DD_FLAG_FW(d)) &&
- comm->vbuf.v[buf_pos][d] > cell_x1[dim]) ||
- ((flag & DD_FLAG_BW(d)) &&
- comm->vbuf.v[buf_pos][d] < cell_x0[dim]))
- {
- cg_move_error(fplog,dd,step,cg,d,
- (flag & DD_FLAG_FW(d)) ? 1 : 0,
- FALSE,0,
- comm->vbuf.v[buf_pos],
- comm->vbuf.v[buf_pos],
- comm->vbuf.v[buf_pos][d]);
- }
- }
-
- mc = -1;
- if (d < dd->ndim-1)
- {
- /* Check which direction this cg should go */
- for(d2=d+1; (d2<dd->ndim && mc==-1); d2++)
- {
- if (dd->bGridJump)
- {
- /* The cell boundaries for dimension d2 are not equal
- * for each cell row of the lower dimension(s),
- * therefore we might need to redetermine where
- * this cg should go.
- */
- dim2 = dd->dim[d2];
- /* If this cg crosses the box boundary in dimension d2
- * we can use the communicated flag, so we do not
- * have to worry about pbc.
- */
- if (!((dd->ci[dim2] == dd->nc[dim2]-1 &&
- (flag & DD_FLAG_FW(d2))) ||
- (dd->ci[dim2] == 0 &&
- (flag & DD_FLAG_BW(d2)))))
- {
- /* Clear the two flags for this dimension */
- flag &= ~(DD_FLAG_FW(d2) | DD_FLAG_BW(d2));
- /* Determine the location of this cg
- * in lattice coordinates
- */
- pos_d = comm->vbuf.v[buf_pos][dim2];
- if (tric_dir[dim2])
- {
- for(d3=dim2+1; d3<DIM; d3++)
- {
- pos_d +=
- comm->vbuf.v[buf_pos][d3]*tcm[d3][dim2];
- }
- }
- /* Check of we are not at the box edge.
- * pbc is only handled in the first step above,
- * but this check could move over pbc while
- * the first step did not due to different rounding.
- */
- if (pos_d >= cell_x1[dim2] &&
- dd->ci[dim2] != dd->nc[dim2]-1)
- {
- flag |= DD_FLAG_FW(d2);
- }
- else if (pos_d < cell_x0[dim2] &&
- dd->ci[dim2] != 0)
- {
- flag |= DD_FLAG_BW(d2);
- }
- comm->buf_int[cg*DD_CGIBS+1] = flag;
- }
- }
- /* Set to which neighboring cell this cg should go */
- if (flag & DD_FLAG_FW(d2))
- {
- mc = d2*2;
- }
- else if (flag & DD_FLAG_BW(d2))
- {
- if (dd->nc[dd->dim[d2]] > 2)
- {
- mc = d2*2+1;
- }
- else
- {
- mc = d2*2;
- }
- }
- }
- }
-
- nrcg = flag & DD_FLAG_NRCG;
- if (mc == -1)
- {
- if (home_pos_cg+1 > dd->cg_nalloc)
- {
- dd->cg_nalloc = over_alloc_dd(home_pos_cg+1);
- srenew(dd->index_gl,dd->cg_nalloc);
- srenew(dd->cgindex,dd->cg_nalloc+1);
- }
- /* Set the global charge group index and size */
- dd->index_gl[home_pos_cg] = comm->buf_int[cg*DD_CGIBS];
- dd->cgindex[home_pos_cg+1] = dd->cgindex[home_pos_cg] + nrcg;
- /* Copy the state from the buffer */
- if (home_pos_cg >= fr->cg_nalloc)
- {
- dd_realloc_fr_cg(fr,home_pos_cg+1);
- cg_cm = fr->cg_cm;
- }
- copy_rvec(comm->vbuf.v[buf_pos++],cg_cm[home_pos_cg]);
- /* Set the cginfo */
- fr->cginfo[home_pos_cg] = ddcginfo(cginfo_mb,
- dd->index_gl[home_pos_cg]);
- if (comm->bLocalCG)
- {
- comm->bLocalCG[dd->index_gl[home_pos_cg]] = TRUE;
- }
-
- if (home_pos_at+nrcg > state->nalloc)
- {
- dd_realloc_state(state,f,home_pos_at+nrcg);
- }
- for(i=0; i<nrcg; i++)
- {
- copy_rvec(comm->vbuf.v[buf_pos++],
- state->x[home_pos_at+i]);
- }
- if (bV)
- {
- for(i=0; i<nrcg; i++)
- {
- copy_rvec(comm->vbuf.v[buf_pos++],
- state->v[home_pos_at+i]);
- }
- }
- if (bSDX)
- {
- for(i=0; i<nrcg; i++)
- {
- copy_rvec(comm->vbuf.v[buf_pos++],
- state->sd_X[home_pos_at+i]);
- }
- }
- if (bCGP)
- {
- for(i=0; i<nrcg; i++)
- {
- copy_rvec(comm->vbuf.v[buf_pos++],
- state->cg_p[home_pos_at+i]);
- }
- }
- home_pos_cg += 1;
- home_pos_at += nrcg;
- }
- else
- {
- /* Reallocate the buffers if necessary */
- if (ncg[mc]+1 > comm->cggl_flag_nalloc[mc])
- {
- comm->cggl_flag_nalloc[mc] = over_alloc_dd(ncg[mc]+1);
- srenew(comm->cggl_flag[mc],comm->cggl_flag_nalloc[mc]*DD_CGIBS);
- }
- nvr = ncg[mc] + nat[mc]*nvec;
- if (nvr + 1 + nrcg*nvec > comm->cgcm_state_nalloc[mc])
- {
- comm->cgcm_state_nalloc[mc] = over_alloc_dd(nvr + 1 + nrcg*nvec);
- srenew(comm->cgcm_state[mc],comm->cgcm_state_nalloc[mc]);
- }
- /* Copy from the receive to the send buffers */
- memcpy(comm->cggl_flag[mc] + ncg[mc]*DD_CGIBS,
- comm->buf_int + cg*DD_CGIBS,
- DD_CGIBS*sizeof(int));
- memcpy(comm->cgcm_state[mc][nvr],
- comm->vbuf.v[buf_pos],
- (1+nrcg*nvec)*sizeof(rvec));
- buf_pos += 1 + nrcg*nvec;
- ncg[mc] += 1;
- nat[mc] += nrcg;
- }
- }
- }
-
- /* With sorting (!bCompact) the indices are now only partially up to date
- * and ncg_home and nat_home are not the real count, since there are
- * "holes" in the arrays for the charge groups that moved to neighbors.
- */
- dd->ncg_home = home_pos_cg;
- dd->nat_home = home_pos_at;
-
- if (debug)
- {
- fprintf(debug,"Finished repartitioning\n");
- }
-
- return ncg_stay_home;
-}
-
-void dd_cycles_add(gmx_domdec_t *dd,float cycles,int ddCycl)
-{
- dd->comm->cycl[ddCycl] += cycles;
- dd->comm->cycl_n[ddCycl]++;
- if (cycles > dd->comm->cycl_max[ddCycl])
- {
- dd->comm->cycl_max[ddCycl] = cycles;
- }
-}
-
-static double force_flop_count(t_nrnb *nrnb)
-{
- int i;
- double sum;
- const char *name;
-
- sum = 0;
- for(i=eNR_NBKERNEL010; i<eNR_NBKERNEL_FREE_ENERGY; i++)
- {
- /* To get closer to the real timings, we half the count
- * for the normal loops and again half it for water loops.
- */
- name = nrnb_str(i);
- if (strstr(name,"W3") != NULL || strstr(name,"W4") != NULL)
- {
- sum += nrnb->n[i]*0.25*cost_nrnb(i);
- }
- else
- {
- sum += nrnb->n[i]*0.50*cost_nrnb(i);
- }
- }
- for(i=eNR_NBKERNEL_FREE_ENERGY; i<=eNR_NB14; i++)
- {
- name = nrnb_str(i);
- if (strstr(name,"W3") != NULL || strstr(name,"W4") != NULL)
- sum += nrnb->n[i]*cost_nrnb(i);
- }
- for(i=eNR_BONDS; i<=eNR_WALLS; i++)
- {
- sum += nrnb->n[i]*cost_nrnb(i);
- }
-
- return sum;
-}
-
-void dd_force_flop_start(gmx_domdec_t *dd,t_nrnb *nrnb)
-{
- if (dd->comm->eFlop)
- {
- dd->comm->flop -= force_flop_count(nrnb);
- }
-}
-void dd_force_flop_stop(gmx_domdec_t *dd,t_nrnb *nrnb)
-{
- if (dd->comm->eFlop)
- {
- dd->comm->flop += force_flop_count(nrnb);
- dd->comm->flop_n++;
- }
-}
-
-static void clear_dd_cycle_counts(gmx_domdec_t *dd)
-{
- int i;
-
- for(i=0; i<ddCyclNr; i++)
- {
- dd->comm->cycl[i] = 0;
- dd->comm->cycl_n[i] = 0;
- dd->comm->cycl_max[i] = 0;
- }
- dd->comm->flop = 0;
- dd->comm->flop_n = 0;
-}
-
-static void get_load_distribution(gmx_domdec_t *dd,gmx_wallcycle_t wcycle)
-{
- gmx_domdec_comm_t *comm;
- gmx_domdec_load_t *load;
- gmx_domdec_root_t *root=NULL;
- int d,dim,cid,i,pos;
- float cell_frac=0,sbuf[DD_NLOAD_MAX];
- gmx_bool bSepPME;
-
- if (debug)
- {
- fprintf(debug,"get_load_distribution start\n");
- }
-
- wallcycle_start(wcycle,ewcDDCOMMLOAD);
-
- comm = dd->comm;
-
- bSepPME = (dd->pme_nodeid >= 0);
-
- for(d=dd->ndim-1; d>=0; d--)
- {
- dim = dd->dim[d];
- /* Check if we participate in the communication in this dimension */
- if (d == dd->ndim-1 ||
- (dd->ci[dd->dim[d+1]]==0 && dd->ci[dd->dim[dd->ndim-1]]==0))
- {
- load = &comm->load[d];
- if (dd->bGridJump)
- {
- cell_frac = comm->cell_f1[d] - comm->cell_f0[d];
- }
- pos = 0;
- if (d == dd->ndim-1)
- {
- sbuf[pos++] = dd_force_load(comm);
- sbuf[pos++] = sbuf[0];
- if (dd->bGridJump)
- {
- sbuf[pos++] = sbuf[0];
- sbuf[pos++] = cell_frac;
- if (d > 0)
- {
- sbuf[pos++] = comm->cell_f_max0[d];
- sbuf[pos++] = comm->cell_f_min1[d];
- }
- }
- if (bSepPME)
- {
- sbuf[pos++] = comm->cycl[ddCyclPPduringPME];
- sbuf[pos++] = comm->cycl[ddCyclPME];
- }
- }
- else
- {
- sbuf[pos++] = comm->load[d+1].sum;
- sbuf[pos++] = comm->load[d+1].max;
- if (dd->bGridJump)
- {
- sbuf[pos++] = comm->load[d+1].sum_m;
- sbuf[pos++] = comm->load[d+1].cvol_min*cell_frac;
- sbuf[pos++] = comm->load[d+1].flags;
- if (d > 0)
- {
- sbuf[pos++] = comm->cell_f_max0[d];
- sbuf[pos++] = comm->cell_f_min1[d];
- }
- }
- if (bSepPME)
- {
- sbuf[pos++] = comm->load[d+1].mdf;
- sbuf[pos++] = comm->load[d+1].pme;
- }
- }
- load->nload = pos;
- /* Communicate a row in DD direction d.
- * The communicators are setup such that the root always has rank 0.
- */
-#ifdef GMX_MPI
- MPI_Gather(sbuf ,load->nload*sizeof(float),MPI_BYTE,
- load->load,load->nload*sizeof(float),MPI_BYTE,
- 0,comm->mpi_comm_load[d]);
-#endif
- if (dd->ci[dim] == dd->master_ci[dim])
- {
- /* We are the root, process this row */
- if (comm->bDynLoadBal)
- {
- root = comm->root[d];
- }
- load->sum = 0;
- load->max = 0;
- load->sum_m = 0;
- load->cvol_min = 1;
- load->flags = 0;
- load->mdf = 0;
- load->pme = 0;
- pos = 0;
- for(i=0; i<dd->nc[dim]; i++)
- {
- load->sum += load->load[pos++];
- load->max = max(load->max,load->load[pos]);
- pos++;
- if (dd->bGridJump)
- {
- if (root->bLimited)
- {
- /* This direction could not be load balanced properly,
- * therefore we need to use the maximum iso the average load.
- */
- load->sum_m = max(load->sum_m,load->load[pos]);
- }
- else
- {
- load->sum_m += load->load[pos];
- }
- pos++;
- load->cvol_min = min(load->cvol_min,load->load[pos]);
- pos++;
- if (d < dd->ndim-1)
- {
- load->flags = (int)(load->load[pos++] + 0.5);
- }
- if (d > 0)
- {
- root->cell_f_max0[i] = load->load[pos++];
- root->cell_f_min1[i] = load->load[pos++];
- }
- }
- if (bSepPME)
- {
- load->mdf = max(load->mdf,load->load[pos]);
- pos++;
- load->pme = max(load->pme,load->load[pos]);
- pos++;
- }
- }
- if (comm->bDynLoadBal && root->bLimited)
- {
- load->sum_m *= dd->nc[dim];
- load->flags |= (1<<d);
- }
- }
- }
- }
-
- if (DDMASTER(dd))
- {
- comm->nload += dd_load_count(comm);
- comm->load_step += comm->cycl[ddCyclStep];
- comm->load_sum += comm->load[0].sum;
- comm->load_max += comm->load[0].max;
- if (comm->bDynLoadBal)
- {
- for(d=0; d<dd->ndim; d++)
- {
- if (comm->load[0].flags & (1<<d))
- {
- comm->load_lim[d]++;
- }
- }
- }
- if (bSepPME)
- {
- comm->load_mdf += comm->load[0].mdf;
- comm->load_pme += comm->load[0].pme;
- }
- }
-
- wallcycle_stop(wcycle,ewcDDCOMMLOAD);
-
- if (debug)
- {
- fprintf(debug,"get_load_distribution finished\n");
- }
-}
-
-static float dd_force_imb_perf_loss(gmx_domdec_t *dd)
-{
- /* Return the relative performance loss on the total run time
- * due to the force calculation load imbalance.
- */
- if (dd->comm->nload > 0)
- {
- return
- (dd->comm->load_max*dd->nnodes - dd->comm->load_sum)/
- (dd->comm->load_step*dd->nnodes);
- }
- else
- {
- return 0;
- }
-}
-
-static void print_dd_load_av(FILE *fplog,gmx_domdec_t *dd)
-{
- char buf[STRLEN];
- int npp,npme,nnodes,d,limp;
- float imbal,pme_f_ratio,lossf,lossp=0;
- gmx_bool bLim;
- gmx_domdec_comm_t *comm;
-
- comm = dd->comm;
- if (DDMASTER(dd) && comm->nload > 0)
- {
- npp = dd->nnodes;
- npme = (dd->pme_nodeid >= 0) ? comm->npmenodes : 0;
- nnodes = npp + npme;
- imbal = comm->load_max*npp/comm->load_sum - 1;
- lossf = dd_force_imb_perf_loss(dd);
- sprintf(buf," Average load imbalance: %.1f %%\n",imbal*100);
- fprintf(fplog,"%s",buf);
- fprintf(stderr,"\n");
- fprintf(stderr,"%s",buf);
- sprintf(buf," Part of the total run time spent waiting due to load imbalance: %.1f %%\n",lossf*100);
- fprintf(fplog,"%s",buf);
- fprintf(stderr,"%s",buf);
- bLim = FALSE;
- if (comm->bDynLoadBal)
- {
- sprintf(buf," Steps where the load balancing was limited by -rdd, -rcon and/or -dds:");
- for(d=0; d<dd->ndim; d++)
- {
- limp = (200*comm->load_lim[d]+1)/(2*comm->nload);
- sprintf(buf+strlen(buf)," %c %d %%",dim2char(dd->dim[d]),limp);
- if (limp >= 50)
- {
- bLim = TRUE;
- }
- }
- sprintf(buf+strlen(buf),"\n");
- fprintf(fplog,"%s",buf);
- fprintf(stderr,"%s",buf);
- }
- if (npme > 0)
- {
- pme_f_ratio = comm->load_pme/comm->load_mdf;
- lossp = (comm->load_pme -comm->load_mdf)/comm->load_step;
- if (lossp <= 0)
- {
- lossp *= (float)npme/(float)nnodes;
- }
- else
- {
- lossp *= (float)npp/(float)nnodes;
- }
- sprintf(buf," Average PME mesh/force load: %5.3f\n",pme_f_ratio);
- fprintf(fplog,"%s",buf);
- fprintf(stderr,"%s",buf);
- sprintf(buf," Part of the total run time spent waiting due to PP/PME imbalance: %.1f %%\n",fabs(lossp)*100);
- fprintf(fplog,"%s",buf);
- fprintf(stderr,"%s",buf);
- }
- fprintf(fplog,"\n");
- fprintf(stderr,"\n");
-
- if (lossf >= DD_PERF_LOSS)
- {
- sprintf(buf,
- "NOTE: %.1f %% performance was lost due to load imbalance\n"
- " in the domain decomposition.\n",lossf*100);
- if (!comm->bDynLoadBal)
- {
- sprintf(buf+strlen(buf)," You might want to use dynamic load balancing (option -dlb.)\n");
- }
- else if (bLim)
- {
- sprintf(buf+strlen(buf)," You might want to decrease the cell size limit (options -rdd, -rcon and/or -dds).\n");
- }
- fprintf(fplog,"%s\n",buf);
- fprintf(stderr,"%s\n",buf);
- }
- if (npme > 0 && fabs(lossp) >= DD_PERF_LOSS)
- {
- sprintf(buf,
- "NOTE: %.1f %% performance was lost because the PME nodes\n"
- " had %s work to do than the PP nodes.\n"
- " You might want to %s the number of PME nodes\n"
- " or %s the cut-off and the grid spacing.\n",
- fabs(lossp*100),
- (lossp < 0) ? "less" : "more",
- (lossp < 0) ? "decrease" : "increase",
- (lossp < 0) ? "decrease" : "increase");
- fprintf(fplog,"%s\n",buf);
- fprintf(stderr,"%s\n",buf);
- }
- }
-}
-
-static float dd_vol_min(gmx_domdec_t *dd)
-{
- return dd->comm->load[0].cvol_min*dd->nnodes;
-}
-
-static gmx_bool dd_load_flags(gmx_domdec_t *dd)
-{
- return dd->comm->load[0].flags;
-}
-
-static float dd_f_imbal(gmx_domdec_t *dd)
-{
- return dd->comm->load[0].max*dd->nnodes/dd->comm->load[0].sum - 1;
-}
-
-static float dd_pme_f_ratio(gmx_domdec_t *dd)
-{
- return dd->comm->load[0].pme/dd->comm->load[0].mdf;
-}
-
-static void dd_print_load(FILE *fplog,gmx_domdec_t *dd,gmx_large_int_t step)
-{
- int flags,d;
- char buf[22];
-
- flags = dd_load_flags(dd);
- if (flags)
- {
- fprintf(fplog,
- "DD load balancing is limited by minimum cell size in dimension");
- for(d=0; d<dd->ndim; d++)
- {
- if (flags & (1<<d))
- {
- fprintf(fplog," %c",dim2char(dd->dim[d]));
- }
- }
- fprintf(fplog,"\n");
- }
- fprintf(fplog,"DD step %s",gmx_step_str(step,buf));
- if (dd->comm->bDynLoadBal)
- {
- fprintf(fplog," vol min/aver %5.3f%c",
- dd_vol_min(dd),flags ? '!' : ' ');
- }
- fprintf(fplog," load imb.: force %4.1f%%",dd_f_imbal(dd)*100);
- if (dd->comm->cycl_n[ddCyclPME])
- {
- fprintf(fplog," pme mesh/force %5.3f",dd_pme_f_ratio(dd));
- }
- fprintf(fplog,"\n\n");
-}
-
-static void dd_print_load_verbose(gmx_domdec_t *dd)
-{
- if (dd->comm->bDynLoadBal)
- {
- fprintf(stderr,"vol %4.2f%c ",
- dd_vol_min(dd),dd_load_flags(dd) ? '!' : ' ');
- }
- fprintf(stderr,"imb F %2d%% ",(int)(dd_f_imbal(dd)*100+0.5));
- if (dd->comm->cycl_n[ddCyclPME])
- {
- fprintf(stderr,"pme/F %4.2f ",dd_pme_f_ratio(dd));
- }
-}
-
-#ifdef GMX_MPI
-static void make_load_communicator(gmx_domdec_t *dd,MPI_Group g_all,
- int dim_ind,ivec loc)
-{
- MPI_Group g_row;
- MPI_Comm c_row;
- int dim,i,*rank;
- ivec loc_c;
- gmx_domdec_root_t *root;
-
- dim = dd->dim[dim_ind];
- copy_ivec(loc,loc_c);
- snew(rank,dd->nc[dim]);
- for(i=0; i<dd->nc[dim]; i++)
- {
- loc_c[dim] = i;
- rank[i] = dd_index(dd->nc,loc_c);
- }
- /* Here we create a new group, that does not necessarily
- * include our process. But MPI_Comm_create needs to be
- * called by all the processes in the original communicator.
- * Calling MPI_Group_free afterwards gives errors, so I assume
- * also the group is needed by all processes. (B. Hess)
- */
- MPI_Group_incl(g_all,dd->nc[dim],rank,&g_row);
- MPI_Comm_create(dd->mpi_comm_all,g_row,&c_row);
- if (c_row != MPI_COMM_NULL)
- {
- /* This process is part of the group */
- dd->comm->mpi_comm_load[dim_ind] = c_row;
- if (dd->comm->eDLB != edlbNO)
- {
- if (dd->ci[dim] == dd->master_ci[dim])
- {
- /* This is the root process of this row */
- snew(dd->comm->root[dim_ind],1);
- root = dd->comm->root[dim_ind];
- snew(root->cell_f,DD_CELL_F_SIZE(dd,dim_ind));
- snew(root->old_cell_f,dd->nc[dim]+1);
- snew(root->bCellMin,dd->nc[dim]);
- if (dim_ind > 0)
- {
- snew(root->cell_f_max0,dd->nc[dim]);
- snew(root->cell_f_min1,dd->nc[dim]);
- snew(root->bound_min,dd->nc[dim]);
- snew(root->bound_max,dd->nc[dim]);
- }
- snew(root->buf_ncd,dd->nc[dim]);
- }
- else
- {
- /* This is not a root process, we only need to receive cell_f */
- snew(dd->comm->cell_f_row,DD_CELL_F_SIZE(dd,dim_ind));
- }
- }
- if (dd->ci[dim] == dd->master_ci[dim])
- {
- snew(dd->comm->load[dim_ind].load,dd->nc[dim]*DD_NLOAD_MAX);
- }
- }
- sfree(rank);
-}
-#endif
-
-static void make_load_communicators(gmx_domdec_t *dd)
-{
-#ifdef GMX_MPI
- MPI_Group g_all;
- int dim0,dim1,i,j;
- ivec loc;
-
- if (debug)
- fprintf(debug,"Making load communicators\n");
-
- MPI_Comm_group(dd->mpi_comm_all,&g_all);
-
- snew(dd->comm->load,dd->ndim);
- snew(dd->comm->mpi_comm_load,dd->ndim);
-
- clear_ivec(loc);
- make_load_communicator(dd,g_all,0,loc);
- if (dd->ndim > 1) {
- dim0 = dd->dim[0];
- for(i=0; i<dd->nc[dim0]; i++) {
- loc[dim0] = i;
- make_load_communicator(dd,g_all,1,loc);
- }
- }
- if (dd->ndim > 2) {
- dim0 = dd->dim[0];
- for(i=0; i<dd->nc[dim0]; i++) {
- loc[dim0] = i;
- dim1 = dd->dim[1];
- for(j=0; j<dd->nc[dim1]; j++) {
- loc[dim1] = j;
- make_load_communicator(dd,g_all,2,loc);
- }
- }
- }
-
- MPI_Group_free(&g_all);
-
- if (debug)
- fprintf(debug,"Finished making load communicators\n");
-#endif
-}
-
-void setup_dd_grid(FILE *fplog,gmx_domdec_t *dd)
-{
- gmx_bool bZYX;
- int d,dim,i,j,m;
- ivec tmp,s;
- int nzone,nzonep;
- ivec dd_zp[DD_MAXIZONE];
- gmx_domdec_zones_t *zones;
- gmx_domdec_ns_ranges_t *izone;
-
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- copy_ivec(dd->ci,tmp);
- tmp[dim] = (tmp[dim] + 1) % dd->nc[dim];
- dd->neighbor[d][0] = ddcoord2ddnodeid(dd,tmp);
- copy_ivec(dd->ci,tmp);
- tmp[dim] = (tmp[dim] - 1 + dd->nc[dim]) % dd->nc[dim];
- dd->neighbor[d][1] = ddcoord2ddnodeid(dd,tmp);
- if (debug)
- {
- fprintf(debug,"DD rank %d neighbor ranks in dir %d are + %d - %d\n",
- dd->rank,dim,
- dd->neighbor[d][0],
- dd->neighbor[d][1]);
- }
- }
-
- if (DDMASTER(dd))
- {
- fprintf(stderr,"Making %dD domain decomposition %d x %d x %d\n",
- dd->ndim,dd->nc[XX],dd->nc[YY],dd->nc[ZZ]);
- }
- if (fplog)
- {
- fprintf(fplog,"\nMaking %dD domain decomposition grid %d x %d x %d, home cell index %d %d %d\n\n",
- dd->ndim,
- dd->nc[XX],dd->nc[YY],dd->nc[ZZ],
- dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
- }
- switch (dd->ndim)
- {
- case 3:
- nzone = dd_z3n;
- nzonep = dd_zp3n;
- for(i=0; i<nzonep; i++)
- {
- copy_ivec(dd_zp3[i],dd_zp[i]);
- }
- break;
- case 2:
- nzone = dd_z2n;
- nzonep = dd_zp2n;
- for(i=0; i<nzonep; i++)
- {
- copy_ivec(dd_zp2[i],dd_zp[i]);
- }
- break;
- case 1:
- nzone = dd_z1n;
- nzonep = dd_zp1n;
- for(i=0; i<nzonep; i++)
- {
- copy_ivec(dd_zp1[i],dd_zp[i]);
- }
- break;
- default:
- gmx_fatal(FARGS,"Can only do 1, 2 or 3D domain decomposition");
- nzone = 0;
- nzonep = 0;
- }
-
- zones = &dd->comm->zones;
-
- for(i=0; i<nzone; i++)
- {
- m = 0;
- clear_ivec(zones->shift[i]);
- for(d=0; d<dd->ndim; d++)
- {
- zones->shift[i][dd->dim[d]] = dd_zo[i][m++];
- }
- }
-
- zones->n = nzone;
- for(i=0; i<nzone; i++)
- {
- for(d=0; d<DIM; d++)
- {
- s[d] = dd->ci[d] - zones->shift[i][d];
- if (s[d] < 0)
- {
- s[d] += dd->nc[d];
- }
- else if (s[d] >= dd->nc[d])
- {
- s[d] -= dd->nc[d];
- }
- }
- }
- zones->nizone = nzonep;
- for(i=0; i<zones->nizone; i++)
- {
- if (dd_zp[i][0] != i)
- {
- gmx_fatal(FARGS,"Internal inconsistency in the dd grid setup");
- }
- izone = &zones->izone[i];
- izone->j0 = dd_zp[i][1];
- izone->j1 = dd_zp[i][2];
- for(dim=0; dim<DIM; dim++)
- {
- if (dd->nc[dim] == 1)
- {
- /* All shifts should be allowed */
- izone->shift0[dim] = -1;
- izone->shift1[dim] = 1;
- }
- else
- {
- /*
- izone->shift0[d] = 0;
- izone->shift1[d] = 0;
- for(j=izone->j0; j<izone->j1; j++) {
- if (dd->shift[j][d] > dd->shift[i][d])
- izone->shift0[d] = -1;
- if (dd->shift[j][d] < dd->shift[i][d])
- izone->shift1[d] = 1;
- }
- */
-
- int shift_diff;
-
- /* Assume the shift are not more than 1 cell */
- izone->shift0[dim] = 1;
- izone->shift1[dim] = -1;
- for(j=izone->j0; j<izone->j1; j++)
- {
- shift_diff = zones->shift[j][dim] - zones->shift[i][dim];
- if (shift_diff < izone->shift0[dim])
- {
- izone->shift0[dim] = shift_diff;
- }
- if (shift_diff > izone->shift1[dim])
- {
- izone->shift1[dim] = shift_diff;
- }
- }
- }
- }
- }
-
- if (dd->comm->eDLB != edlbNO)
- {
- snew(dd->comm->root,dd->ndim);
- }
-
- if (dd->comm->bRecordLoad)
- {
- make_load_communicators(dd);
- }
-}
-
-static void make_pp_communicator(FILE *fplog,t_commrec *cr,int reorder)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- int i,rank,*buf;
- ivec periods;
-#ifdef GMX_MPI
- MPI_Comm comm_cart;
-#endif
-
- dd = cr->dd;
- comm = dd->comm;
-
-#ifdef GMX_MPI
- if (comm->bCartesianPP)
- {
- /* Set up cartesian communication for the particle-particle part */
- if (fplog)
- {
- fprintf(fplog,"Will use a Cartesian communicator: %d x %d x %d\n",
- dd->nc[XX],dd->nc[YY],dd->nc[ZZ]);
- }
-
- for(i=0; i<DIM; i++)
- {
- periods[i] = TRUE;
- }
- MPI_Cart_create(cr->mpi_comm_mygroup,DIM,dd->nc,periods,reorder,
- &comm_cart);
- /* We overwrite the old communicator with the new cartesian one */
- cr->mpi_comm_mygroup = comm_cart;
- }
-
- dd->mpi_comm_all = cr->mpi_comm_mygroup;
- MPI_Comm_rank(dd->mpi_comm_all,&dd->rank);
-
- if (comm->bCartesianPP_PME)
- {
- /* Since we want to use the original cartesian setup for sim,
- * and not the one after split, we need to make an index.
- */
- snew(comm->ddindex2ddnodeid,dd->nnodes);
- comm->ddindex2ddnodeid[dd_index(dd->nc,dd->ci)] = dd->rank;
- gmx_sumi(dd->nnodes,comm->ddindex2ddnodeid,cr);
- /* Get the rank of the DD master,
- * above we made sure that the master node is a PP node.
- */
- if (MASTER(cr))
- {
- rank = dd->rank;
- }
- else
- {
- rank = 0;
- }
- MPI_Allreduce(&rank,&dd->masterrank,1,MPI_INT,MPI_SUM,dd->mpi_comm_all);
- }
- else if (comm->bCartesianPP)
- {
- if (cr->npmenodes == 0)
- {
- /* The PP communicator is also
- * the communicator for this simulation
- */
- cr->mpi_comm_mysim = cr->mpi_comm_mygroup;
- }
- cr->nodeid = dd->rank;
-
- MPI_Cart_coords(dd->mpi_comm_all,dd->rank,DIM,dd->ci);
-
- /* We need to make an index to go from the coordinates
- * to the nodeid of this simulation.
- */
- snew(comm->ddindex2simnodeid,dd->nnodes);
- snew(buf,dd->nnodes);
- if (cr->duty & DUTY_PP)
- {
- buf[dd_index(dd->nc,dd->ci)] = cr->sim_nodeid;
- }
- /* Communicate the ddindex to simulation nodeid index */
- MPI_Allreduce(buf,comm->ddindex2simnodeid,dd->nnodes,MPI_INT,MPI_SUM,
- cr->mpi_comm_mysim);
- sfree(buf);
-
- /* Determine the master coordinates and rank.
- * The DD master should be the same node as the master of this sim.
- */
- for(i=0; i<dd->nnodes; i++)
- {
- if (comm->ddindex2simnodeid[i] == 0)
- {
- ddindex2xyz(dd->nc,i,dd->master_ci);
- MPI_Cart_rank(dd->mpi_comm_all,dd->master_ci,&dd->masterrank);
- }
- }
- if (debug)
- {
- fprintf(debug,"The master rank is %d\n",dd->masterrank);
- }
- }
- else
- {
- /* No Cartesian communicators */
- /* We use the rank in dd->comm->all as DD index */
- ddindex2xyz(dd->nc,dd->rank,dd->ci);
- /* The simulation master nodeid is 0, so the DD master rank is also 0 */
- dd->masterrank = 0;
- clear_ivec(dd->master_ci);
- }
-#endif
-
- if (fplog)
- {
- fprintf(fplog,
- "Domain decomposition nodeid %d, coordinates %d %d %d\n\n",
- dd->rank,dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
- }
- if (debug)
- {
- fprintf(debug,
- "Domain decomposition nodeid %d, coordinates %d %d %d\n\n",
- dd->rank,dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
- }
-}
-
-static void receive_ddindex2simnodeid(t_commrec *cr)
-{
- gmx_domdec_t *dd;
-
- gmx_domdec_comm_t *comm;
- int *buf;
-
- dd = cr->dd;
- comm = dd->comm;
-
-#ifdef GMX_MPI
- if (!comm->bCartesianPP_PME && comm->bCartesianPP)
- {
- snew(comm->ddindex2simnodeid,dd->nnodes);
- snew(buf,dd->nnodes);
- if (cr->duty & DUTY_PP)
- {
- buf[dd_index(dd->nc,dd->ci)] = cr->sim_nodeid;
- }
-#ifdef GMX_MPI
- /* Communicate the ddindex to simulation nodeid index */
- MPI_Allreduce(buf,comm->ddindex2simnodeid,dd->nnodes,MPI_INT,MPI_SUM,
- cr->mpi_comm_mysim);
-#endif
- sfree(buf);
- }
-#endif
-}
-
-static gmx_domdec_master_t *init_gmx_domdec_master_t(gmx_domdec_t *dd,
- int ncg,int natoms)
-{
- gmx_domdec_master_t *ma;
- int i;
-
- snew(ma,1);
-
- snew(ma->ncg,dd->nnodes);
- snew(ma->index,dd->nnodes+1);
- snew(ma->cg,ncg);
- snew(ma->nat,dd->nnodes);
- snew(ma->ibuf,dd->nnodes*2);
- snew(ma->cell_x,DIM);
- for(i=0; i<DIM; i++)
- {
- snew(ma->cell_x[i],dd->nc[i]+1);
- }
-
- if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
- {
- ma->vbuf = NULL;
- }
- else
- {
- snew(ma->vbuf,natoms);
- }
-
- return ma;
-}
-
-static void split_communicator(FILE *fplog,t_commrec *cr,int dd_node_order,
- int reorder)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- int i,rank;
- gmx_bool bDiv[DIM];
- ivec periods;
-#ifdef GMX_MPI
- MPI_Comm comm_cart;
-#endif
-
- dd = cr->dd;
- comm = dd->comm;
-
- if (comm->bCartesianPP)
- {
- for(i=1; i<DIM; i++)
- {
- bDiv[i] = ((cr->npmenodes*dd->nc[i]) % (dd->nnodes) == 0);
- }
- if (bDiv[YY] || bDiv[ZZ])
- {
- comm->bCartesianPP_PME = TRUE;
- /* If we have 2D PME decomposition, which is always in x+y,
- * we stack the PME only nodes in z.
- * Otherwise we choose the direction that provides the thinnest slab
- * of PME only nodes as this will have the least effect
- * on the PP communication.
- * But for the PME communication the opposite might be better.
- */
- if (bDiv[ZZ] && (comm->npmenodes_y > 1 ||
- !bDiv[YY] ||
- dd->nc[YY] > dd->nc[ZZ]))
- {
- comm->cartpmedim = ZZ;
- }
- else
- {
- comm->cartpmedim = YY;
- }
- comm->ntot[comm->cartpmedim]
- += (cr->npmenodes*dd->nc[comm->cartpmedim])/dd->nnodes;
- }
- else if (fplog)
- {
- fprintf(fplog,"#pmenodes (%d) is not a multiple of nx*ny (%d*%d) or nx*nz (%d*%d)\n",cr->npmenodes,dd->nc[XX],dd->nc[YY],dd->nc[XX],dd->nc[ZZ]);
- fprintf(fplog,
- "Will not use a Cartesian communicator for PP <-> PME\n\n");
- }
- }
-
-#ifdef GMX_MPI
- if (comm->bCartesianPP_PME)
- {
- if (fplog)
- {
- fprintf(fplog,"Will use a Cartesian communicator for PP <-> PME: %d x %d x %d\n",comm->ntot[XX],comm->ntot[YY],comm->ntot[ZZ]);
- }
-
- for(i=0; i<DIM; i++)
- {
- periods[i] = TRUE;
- }
- MPI_Cart_create(cr->mpi_comm_mysim,DIM,comm->ntot,periods,reorder,
- &comm_cart);
-
- MPI_Comm_rank(comm_cart,&rank);
- if (MASTERNODE(cr) && rank != 0)
- {
- gmx_fatal(FARGS,"MPI rank 0 was renumbered by MPI_Cart_create, we do not allow this");
- }
-
- /* With this assigment we loose the link to the original communicator
- * which will usually be MPI_COMM_WORLD, unless have multisim.
- */
- cr->mpi_comm_mysim = comm_cart;
- cr->sim_nodeid = rank;
-
- MPI_Cart_coords(cr->mpi_comm_mysim,cr->sim_nodeid,DIM,dd->ci);
-
- if (fplog)
- {
- fprintf(fplog,"Cartesian nodeid %d, coordinates %d %d %d\n\n",
- cr->sim_nodeid,dd->ci[XX],dd->ci[YY],dd->ci[ZZ]);
- }
-
- if (dd->ci[comm->cartpmedim] < dd->nc[comm->cartpmedim])
- {
- cr->duty = DUTY_PP;
- }
- if (cr->npmenodes == 0 ||
- dd->ci[comm->cartpmedim] >= dd->nc[comm->cartpmedim])
- {
- cr->duty = DUTY_PME;
- }
-
- /* Split the sim communicator into PP and PME only nodes */
- MPI_Comm_split(cr->mpi_comm_mysim,
- cr->duty,
- dd_index(comm->ntot,dd->ci),
- &cr->mpi_comm_mygroup);
- }
- else
- {
- switch (dd_node_order)
- {
- case ddnoPP_PME:
- if (fplog)
- {
- fprintf(fplog,"Order of the nodes: PP first, PME last\n");
- }
- break;
- case ddnoINTERLEAVE:
- /* Interleave the PP-only and PME-only nodes,
- * as on clusters with dual-core machines this will double
- * the communication bandwidth of the PME processes
- * and thus speed up the PP <-> PME and inter PME communication.
- */
- if (fplog)
- {
- fprintf(fplog,"Interleaving PP and PME nodes\n");
- }
- comm->pmenodes = dd_pmenodes(cr);
- break;
- case ddnoCARTESIAN:
- break;
- default:
- gmx_fatal(FARGS,"Unknown dd_node_order=%d",dd_node_order);
- }
-
- if (dd_simnode2pmenode(cr,cr->sim_nodeid) == -1)
- {
- cr->duty = DUTY_PME;
- }
- else
- {
- cr->duty = DUTY_PP;
- }
-
- /* Split the sim communicator into PP and PME only nodes */
- MPI_Comm_split(cr->mpi_comm_mysim,
- cr->duty,
- cr->nodeid,
- &cr->mpi_comm_mygroup);
- MPI_Comm_rank(cr->mpi_comm_mygroup,&cr->nodeid);
- }
-#endif
-
- if (fplog)
- {
- fprintf(fplog,"This is a %s only node\n\n",
- (cr->duty & DUTY_PP) ? "particle-particle" : "PME-mesh");
- }
-}
-
-void make_dd_communicators(FILE *fplog,t_commrec *cr,int dd_node_order)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- int CartReorder;
-
- dd = cr->dd;
- comm = dd->comm;
-
- copy_ivec(dd->nc,comm->ntot);
-
- comm->bCartesianPP = (dd_node_order == ddnoCARTESIAN);
- comm->bCartesianPP_PME = FALSE;
-
- /* Reorder the nodes by default. This might change the MPI ranks.
- * Real reordering is only supported on very few architectures,
- * Blue Gene is one of them.
- */
- CartReorder = (getenv("GMX_NO_CART_REORDER") == NULL);
-
- if (cr->npmenodes > 0)
- {
- /* Split the communicator into a PP and PME part */
- split_communicator(fplog,cr,dd_node_order,CartReorder);
- if (comm->bCartesianPP_PME)
- {
- /* We (possibly) reordered the nodes in split_communicator,
- * so it is no longer required in make_pp_communicator.
- */
- CartReorder = FALSE;
- }
- }
- else
- {
- /* All nodes do PP and PME */
-#ifdef GMX_MPI
- /* We do not require separate communicators */
- cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
-#endif
- }
-
- if (cr->duty & DUTY_PP)
- {
- /* Copy or make a new PP communicator */
- make_pp_communicator(fplog,cr,CartReorder);
- }
- else
- {
- receive_ddindex2simnodeid(cr);
- }
-
- if (!(cr->duty & DUTY_PME))
- {
- /* Set up the commnuication to our PME node */
- dd->pme_nodeid = dd_simnode2pmenode(cr,cr->sim_nodeid);
- dd->pme_receive_vir_ener = receive_vir_ener(cr);
- if (debug)
- {
- fprintf(debug,"My pme_nodeid %d receive ener %d\n",
- dd->pme_nodeid,dd->pme_receive_vir_ener);
- }
- }
- else
- {
- dd->pme_nodeid = -1;
- }
-
- if (DDMASTER(dd))
- {
- dd->ma = init_gmx_domdec_master_t(dd,
- comm->cgs_gl.nr,
- comm->cgs_gl.index[comm->cgs_gl.nr]);
- }
-}
-
-static real *get_slb_frac(FILE *fplog,const char *dir,int nc,const char *size_string)
-{
- real *slb_frac,tot;
- int i,n;
- double dbl;
-
- slb_frac = NULL;
- if (nc > 1 && size_string != NULL)
- {
- if (fplog)
- {
- fprintf(fplog,"Using static load balancing for the %s direction\n",
- dir);
- }
- snew(slb_frac,nc);
- tot = 0;
- for (i=0; i<nc; i++)
- {
- dbl = 0;
- sscanf(size_string,"%lf%n",&dbl,&n);
- if (dbl == 0)
- {
- gmx_fatal(FARGS,"Incorrect or not enough DD cell size entries for direction %s: '%s'",dir,size_string);
- }
- slb_frac[i] = dbl;
- size_string += n;
- tot += slb_frac[i];
- }
- /* Normalize */
- if (fplog)
- {
- fprintf(fplog,"Relative cell sizes:");
- }
- for (i=0; i<nc; i++)
- {
- slb_frac[i] /= tot;
- if (fplog)
- {
- fprintf(fplog," %5.3f",slb_frac[i]);
- }
- }
- if (fplog)
- {
- fprintf(fplog,"\n");
- }
- }
-
- return slb_frac;
-}
-
-static int multi_body_bondeds_count(gmx_mtop_t *mtop)
-{
- int n,nmol,ftype;
- gmx_mtop_ilistloop_t iloop;
- t_ilist *il;
-
- n = 0;
- iloop = gmx_mtop_ilistloop_init(mtop);
- while (gmx_mtop_ilistloop_next(iloop,&il,&nmol))
- {
- for(ftype=0; ftype<F_NRE; ftype++)
- {
- if ((interaction_function[ftype].flags & IF_BOND) &&
- NRAL(ftype) > 2)
- {
- n += nmol*il[ftype].nr/(1 + NRAL(ftype));
- }
- }
- }
-
- return n;
-}
-
-static int dd_nst_env(FILE *fplog,const char *env_var,int def)
-{
- char *val;
- int nst;
-
- nst = def;
- val = getenv(env_var);
- if (val)
- {
- if (sscanf(val,"%d",&nst) <= 0)
- {
- nst = 1;
- }
- if (fplog)
- {
- fprintf(fplog,"Found env.var. %s = %s, using value %d\n",
- env_var,val,nst);
- }
- }
-
- return nst;
-}
-
-static void dd_warning(t_commrec *cr,FILE *fplog,const char *warn_string)
-{
- if (MASTER(cr))
- {
- fprintf(stderr,"\n%s\n",warn_string);
- }
- if (fplog)
- {
- fprintf(fplog,"\n%s\n",warn_string);
- }
-}
-
-static void check_dd_restrictions(t_commrec *cr,gmx_domdec_t *dd,
- t_inputrec *ir,FILE *fplog)
-{
- if (ir->ePBC == epbcSCREW &&
- (dd->nc[XX] == 1 || dd->nc[YY] > 1 || dd->nc[ZZ] > 1))
- {
- gmx_fatal(FARGS,"With pbc=%s can only do domain decomposition in the x-direction",epbc_names[ir->ePBC]);
- }
-
- if (ir->ns_type == ensSIMPLE)
- {
- gmx_fatal(FARGS,"Domain decomposition does not support simple neighbor searching, use grid searching or use particle decomposition");
- }
-
- if (ir->nstlist == 0)
- {
- gmx_fatal(FARGS,"Domain decomposition does not work with nstlist=0");
- }
-
- if (ir->comm_mode == ecmANGULAR && ir->ePBC != epbcNONE)
- {
- dd_warning(cr,fplog,"comm-mode angular will give incorrect results when the comm group partially crosses a periodic boundary");
- }
-}
-
-static real average_cellsize_min(gmx_domdec_t *dd,gmx_ddbox_t *ddbox)
-{
- int di,d;
- real r;
-
- r = ddbox->box_size[XX];
- for(di=0; di<dd->ndim; di++)
- {
- d = dd->dim[di];
- /* Check using the initial average cell size */
- r = min(r,ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
- }
-
- return r;
-}
-
-static int check_dlb_support(FILE *fplog,t_commrec *cr,
- const char *dlb_opt,gmx_bool bRecordLoad,
- unsigned long Flags,t_inputrec *ir)
-{
- gmx_domdec_t *dd;
- int eDLB=-1;
- char buf[STRLEN];
-
- switch (dlb_opt[0])
- {
- case 'a': eDLB = edlbAUTO; break;
- case 'n': eDLB = edlbNO; break;
- case 'y': eDLB = edlbYES; break;
- default: gmx_incons("Unknown dlb_opt");
- }
-
- if (Flags & MD_RERUN)
- {
- return edlbNO;
- }
-
- if (!EI_DYNAMICS(ir->eI))
- {
- if (eDLB == edlbYES)
- {
- sprintf(buf,"NOTE: dynamic load balancing is only supported with dynamics, not with integrator '%s'\n",EI(ir->eI));
- dd_warning(cr,fplog,buf);
- }
-
- return edlbNO;
- }
-
- if (!bRecordLoad)
- {
- dd_warning(cr,fplog,"NOTE: Cycle counting is not supported on this architecture, will not use dynamic load balancing\n");
-
- return edlbNO;
- }
-
- if (Flags & MD_REPRODUCIBLE)
- {
- switch (eDLB)
- {
- case edlbNO:
- break;
- case edlbAUTO:
- dd_warning(cr,fplog,"NOTE: reproducability requested, will not use dynamic load balancing\n");
- eDLB = edlbNO;
- break;
- case edlbYES:
- dd_warning(cr,fplog,"WARNING: reproducability requested with dynamic load balancing, the simulation will NOT be binary reproducable\n");
- break;
- default:
- gmx_fatal(FARGS,"Death horror: undefined case (%d) for load balancing choice",eDLB);
- break;
- }
- }
-
- return eDLB;
-}
-
-static void set_dd_dim(FILE *fplog,gmx_domdec_t *dd)
-{
- int dim;
-
- dd->ndim = 0;
- if (getenv("GMX_DD_ORDER_ZYX") != NULL)
- {
- /* Decomposition order z,y,x */
- if (fplog)
- {
- fprintf(fplog,"Using domain decomposition order z, y, x\n");
- }
- for(dim=DIM-1; dim>=0; dim--)
- {
- if (dd->nc[dim] > 1)
- {
- dd->dim[dd->ndim++] = dim;
- }
- }
- }
- else
- {
- /* Decomposition order x,y,z */
- for(dim=0; dim<DIM; dim++)
- {
- if (dd->nc[dim] > 1)
- {
- dd->dim[dd->ndim++] = dim;
- }
- }
- }
-}
-
-static gmx_domdec_comm_t *init_dd_comm()
-{
- gmx_domdec_comm_t *comm;
- int i;
-
- snew(comm,1);
- snew(comm->cggl_flag,DIM*2);
- snew(comm->cgcm_state,DIM*2);
- for(i=0; i<DIM*2; i++)
- {
- comm->cggl_flag_nalloc[i] = 0;
- comm->cgcm_state_nalloc[i] = 0;
- }
-
- comm->nalloc_int = 0;
- comm->buf_int = NULL;
-
- vec_rvec_init(&comm->vbuf);
-
- comm->n_load_have = 0;
- comm->n_load_collect = 0;
-
- for(i=0; i<ddnatNR-ddnatZONE; i++)
- {
- comm->sum_nat[i] = 0;
- }
- comm->ndecomp = 0;
- comm->nload = 0;
- comm->load_step = 0;
- comm->load_sum = 0;
- comm->load_max = 0;
- clear_ivec(comm->load_lim);
- comm->load_mdf = 0;
- comm->load_pme = 0;
-
- return comm;
-}
-
-gmx_domdec_t *init_domain_decomposition(FILE *fplog,t_commrec *cr,
- unsigned long Flags,
- ivec nc,
- real comm_distance_min,real rconstr,
- const char *dlb_opt,real dlb_scale,
- const char *sizex,const char *sizey,const char *sizez,
- gmx_mtop_t *mtop,t_inputrec *ir,
- matrix box,rvec *x,
- gmx_ddbox_t *ddbox,
- int *npme_x,int *npme_y)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- int recload;
- int d,i,j;
- real r_2b,r_mb,r_bonded=-1,r_bonded_limit=-1,limit,acs;
- gmx_bool bC;
- char buf[STRLEN];
-
- if (fplog)
- {
- fprintf(fplog,
- "\nInitializing Domain Decomposition on %d nodes\n",cr->nnodes);
- }
-
- snew(dd,1);
-
- dd->comm = init_dd_comm();
- comm = dd->comm;
- snew(comm->cggl_flag,DIM*2);
- snew(comm->cgcm_state,DIM*2);
-
- dd->npbcdim = ePBC2npbcdim(ir->ePBC);
- dd->bScrewPBC = (ir->ePBC == epbcSCREW);
-
- dd->bSendRecv2 = dd_nst_env(fplog,"GMX_DD_SENDRECV2",0);
- comm->eFlop = dd_nst_env(fplog,"GMX_DLB_FLOP",0);
- recload = dd_nst_env(fplog,"GMX_DD_LOAD",1);
- comm->nstSortCG = dd_nst_env(fplog,"GMX_DD_SORT",1);
- comm->nstDDDump = dd_nst_env(fplog,"GMX_DD_DUMP",0);
- comm->nstDDDumpGrid = dd_nst_env(fplog,"GMX_DD_DUMP_GRID",0);
- comm->DD_debug = dd_nst_env(fplog,"GMX_DD_DEBUG",0);
-
- dd->pme_recv_f_alloc = 0;
- dd->pme_recv_f_buf = NULL;
-
- if (dd->bSendRecv2 && fplog)
- {
- fprintf(fplog,"Will use two sequential MPI_Sendrecv calls instead of two simultaneous non-blocking MPI_Irecv and MPI_Isend pairs for constraint and vsite communication\n");
- }
- if (comm->eFlop)
- {
- if (fplog)
- {
- fprintf(fplog,"Will load balance based on FLOP count\n");
- }
- if (comm->eFlop > 1)
- {
- srand(1+cr->nodeid);
- }
- comm->bRecordLoad = TRUE;
- }
- else
- {
- comm->bRecordLoad = (wallcycle_have_counter() && recload > 0);
-
- }
-
- comm->eDLB = check_dlb_support(fplog,cr,dlb_opt,comm->bRecordLoad,Flags,ir);
-
- comm->bDynLoadBal = (comm->eDLB == edlbYES);
- if (fplog)
- {
- fprintf(fplog,"Dynamic load balancing: %s\n",edlb_names[comm->eDLB]);
- }
- dd->bGridJump = comm->bDynLoadBal;
-
- if (comm->nstSortCG)
- {
- if (fplog)
- {
- if (comm->nstSortCG == 1)
- {
- fprintf(fplog,"Will sort the charge groups at every domain (re)decomposition\n");
- }
- else
- {
- fprintf(fplog,"Will sort the charge groups every %d steps\n",
- comm->nstSortCG);
- }
- }
- snew(comm->sort,1);
- }
- else
- {
- if (fplog)
- {
- fprintf(fplog,"Will not sort the charge groups\n");
- }
- }
-
- comm->bInterCGBondeds = (ncg_mtop(mtop) > mtop->mols.nr);
- if (comm->bInterCGBondeds)
- {
- comm->bInterCGMultiBody = (multi_body_bondeds_count(mtop) > 0);
- }
- else
- {
- comm->bInterCGMultiBody = FALSE;
- }
-
- dd->bInterCGcons = inter_charge_group_constraints(mtop);
-
- if (ir->rlistlong == 0)
- {
- /* Set the cut-off to some very large value,
- * so we don't need if statements everywhere in the code.
- * We use sqrt, since the cut-off is squared in some places.
- */
- comm->cutoff = GMX_CUTOFF_INF;
- }
- else
- {
- comm->cutoff = ir->rlistlong;
- }
- comm->cutoff_mbody = 0;
-
- comm->cellsize_limit = 0;
- comm->bBondComm = FALSE;
-
- if (comm->bInterCGBondeds)
- {
- if (comm_distance_min > 0)
- {
- comm->cutoff_mbody = comm_distance_min;
- if (Flags & MD_DDBONDCOMM)
- {
- comm->bBondComm = (comm->cutoff_mbody > comm->cutoff);
- }
- else
- {
- comm->cutoff = max(comm->cutoff,comm->cutoff_mbody);
- }
- r_bonded_limit = comm->cutoff_mbody;
- }
- else if (ir->bPeriodicMols)
- {
- /* Can not easily determine the required cut-off */
- dd_warning(cr,fplog,"NOTE: Periodic molecules: can not easily determine the required minimum bonded cut-off, using half the non-bonded cut-off\n");
- comm->cutoff_mbody = comm->cutoff/2;
- r_bonded_limit = comm->cutoff_mbody;
- }
- else
- {
- if (MASTER(cr))
- {
- dd_bonded_cg_distance(fplog,dd,mtop,ir,x,box,
- Flags & MD_DDBONDCHECK,&r_2b,&r_mb);
- }
- gmx_bcast(sizeof(r_2b),&r_2b,cr);
- gmx_bcast(sizeof(r_mb),&r_mb,cr);
-
- /* We use an initial margin of 10% for the minimum cell size,
- * except when we are just below the non-bonded cut-off.
- */
- if (Flags & MD_DDBONDCOMM)
- {
- if (max(r_2b,r_mb) > comm->cutoff)
- {
- r_bonded = max(r_2b,r_mb);
- r_bonded_limit = 1.1*r_bonded;
- comm->bBondComm = TRUE;
- }
- else
- {
- r_bonded = r_mb;
- r_bonded_limit = min(1.1*r_bonded,comm->cutoff);
- }
- /* We determine cutoff_mbody later */
- }
- else
- {
- /* No special bonded communication,
- * simply increase the DD cut-off.
- */
- r_bonded_limit = 1.1*max(r_2b,r_mb);
- comm->cutoff_mbody = r_bonded_limit;
- comm->cutoff = max(comm->cutoff,comm->cutoff_mbody);
- }
- }
- comm->cellsize_limit = max(comm->cellsize_limit,r_bonded_limit);
- if (fplog)
- {
- fprintf(fplog,
- "Minimum cell size due to bonded interactions: %.3f nm\n",
- comm->cellsize_limit);
- }
- }
-
- if (dd->bInterCGcons && rconstr <= 0)
- {
- /* There is a cell size limit due to the constraints (P-LINCS) */
- rconstr = constr_r_max(fplog,mtop,ir);
- if (fplog)
- {
- fprintf(fplog,
- "Estimated maximum distance required for P-LINCS: %.3f nm\n",
- rconstr);
- if (rconstr > comm->cellsize_limit)
- {
- fprintf(fplog,"This distance will limit the DD cell size, you can override this with -rcon\n");
- }
- }
- }
- else if (rconstr > 0 && fplog)
- {
- /* Here we do not check for dd->bInterCGcons,
- * because one can also set a cell size limit for virtual sites only
- * and at this point we don't know yet if there are intercg v-sites.
- */
- fprintf(fplog,
- "User supplied maximum distance required for P-LINCS: %.3f nm\n",
- rconstr);
- }
- comm->cellsize_limit = max(comm->cellsize_limit,rconstr);
-
- comm->cgs_gl = gmx_mtop_global_cgs(mtop);
-
- if (nc[XX] > 0)
- {
- copy_ivec(nc,dd->nc);
- set_dd_dim(fplog,dd);
- set_ddbox_cr(cr,&dd->nc,ir,box,&comm->cgs_gl,x,ddbox);
-
- if (cr->npmenodes == -1)
- {
- cr->npmenodes = 0;
- }
- acs = average_cellsize_min(dd,ddbox);
- if (acs < comm->cellsize_limit)
- {
- if (fplog)
- {
- fprintf(fplog,"ERROR: The initial cell size (%f) is smaller than the cell size limit (%f)\n",acs,comm->cellsize_limit);
- }
- gmx_fatal_collective(FARGS,cr,NULL,
- "The initial cell size (%f) is smaller than the cell size limit (%f), change options -dd, -rdd or -rcon, see the log file for details",
- acs,comm->cellsize_limit);
- }
- }
- else
- {
- set_ddbox_cr(cr,NULL,ir,box,&comm->cgs_gl,x,ddbox);
-
- /* We need to choose the optimal DD grid and possibly PME nodes */
- limit = dd_choose_grid(fplog,cr,dd,ir,mtop,box,ddbox,
- comm->eDLB!=edlbNO,dlb_scale,
- comm->cellsize_limit,comm->cutoff,
- comm->bInterCGBondeds,comm->bInterCGMultiBody);
-
- if (dd->nc[XX] == 0)
- {
- bC = (dd->bInterCGcons && rconstr > r_bonded_limit);
- sprintf(buf,"Change the number of nodes or mdrun option %s%s%s",
- !bC ? "-rdd" : "-rcon",
- comm->eDLB!=edlbNO ? " or -dds" : "",
- bC ? " or your LINCS settings" : "");
-
- gmx_fatal_collective(FARGS,cr,NULL,
- "There is no domain decomposition for %d nodes that is compatible with the given box and a minimum cell size of %g nm\n"
- "%s\n"
- "Look in the log file for details on the domain decomposition",
- cr->nnodes-cr->npmenodes,limit,buf);
- }
- set_dd_dim(fplog,dd);
- }
-
- if (fplog)
- {
- fprintf(fplog,
- "Domain decomposition grid %d x %d x %d, separate PME nodes %d\n",
- dd->nc[XX],dd->nc[YY],dd->nc[ZZ],cr->npmenodes);
- }
-
- dd->nnodes = dd->nc[XX]*dd->nc[YY]*dd->nc[ZZ];
- if (cr->nnodes - dd->nnodes != cr->npmenodes)
- {
- gmx_fatal_collective(FARGS,cr,NULL,
- "The size of the domain decomposition grid (%d) does not match the number of nodes (%d). The total number of nodes is %d",
- dd->nnodes,cr->nnodes - cr->npmenodes,cr->nnodes);
- }
- if (cr->npmenodes > dd->nnodes)
- {
- gmx_fatal_collective(FARGS,cr,NULL,
- "The number of separate PME node (%d) is larger than the number of PP nodes (%d), this is not supported.",cr->npmenodes,dd->nnodes);
- }
- if (cr->npmenodes > 0)
- {
- comm->npmenodes = cr->npmenodes;
- }
- else
- {
- comm->npmenodes = dd->nnodes;
- }
-
- if (EEL_PME(ir->coulombtype))
- {
- /* The following choices should match those
- * in comm_cost_est in domdec_setup.c.
- * Note that here the checks have to take into account
- * that the decomposition might occur in a different order than xyz
- * (for instance through the env.var. GMX_DD_ORDER_ZYX),
- * in which case they will not match those in comm_cost_est,
- * but since that is mainly for testing purposes that's fine.
- */
- if (dd->ndim >= 2 && dd->dim[0] == XX && dd->dim[1] == YY &&
- comm->npmenodes > dd->nc[XX] && comm->npmenodes % dd->nc[XX] == 0 &&
- getenv("GMX_PMEONEDD") == NULL)
- {
- comm->npmedecompdim = 2;
- comm->npmenodes_x = dd->nc[XX];
- comm->npmenodes_y = comm->npmenodes/comm->npmenodes_x;
- }
- else
- {
- /* In case nc is 1 in both x and y we could still choose to
- * decompose pme in y instead of x, but we use x for simplicity.
- */
- comm->npmedecompdim = 1;
- if (dd->dim[0] == YY)
- {
- comm->npmenodes_x = 1;
- comm->npmenodes_y = comm->npmenodes;
- }
- else
- {
- comm->npmenodes_x = comm->npmenodes;
- comm->npmenodes_y = 1;
- }
- }
- if (fplog)
- {
- fprintf(fplog,"PME domain decomposition: %d x %d x %d\n",
- comm->npmenodes_x,comm->npmenodes_y,1);
- }
- }
- else
- {
- comm->npmedecompdim = 0;
- comm->npmenodes_x = 0;
- comm->npmenodes_y = 0;
- }
-
- /* Technically we don't need both of these,
- * but it simplifies code not having to recalculate it.
- */
- *npme_x = comm->npmenodes_x;
- *npme_y = comm->npmenodes_y;
-
- snew(comm->slb_frac,DIM);
- if (comm->eDLB == edlbNO)
- {
- comm->slb_frac[XX] = get_slb_frac(fplog,"x",dd->nc[XX],sizex);
- comm->slb_frac[YY] = get_slb_frac(fplog,"y",dd->nc[YY],sizey);
- comm->slb_frac[ZZ] = get_slb_frac(fplog,"z",dd->nc[ZZ],sizez);
- }
-
- if (comm->bInterCGBondeds && comm->cutoff_mbody == 0)
- {
- if (comm->bBondComm || comm->eDLB != edlbNO)
- {
- /* Set the bonded communication distance to halfway
- * the minimum and the maximum,
- * since the extra communication cost is nearly zero.
- */
- acs = average_cellsize_min(dd,ddbox);
- comm->cutoff_mbody = 0.5*(r_bonded + acs);
- if (comm->eDLB != edlbNO)
- {
- /* Check if this does not limit the scaling */
- comm->cutoff_mbody = min(comm->cutoff_mbody,dlb_scale*acs);
- }
- if (!comm->bBondComm)
- {
- /* Without bBondComm do not go beyond the n.b. cut-off */
- comm->cutoff_mbody = min(comm->cutoff_mbody,comm->cutoff);
- if (comm->cellsize_limit >= comm->cutoff)
- {
- /* We don't loose a lot of efficieny
- * when increasing it to the n.b. cut-off.
- * It can even be slightly faster, because we need
- * less checks for the communication setup.
- */
- comm->cutoff_mbody = comm->cutoff;
- }
- }
- /* Check if we did not end up below our original limit */
- comm->cutoff_mbody = max(comm->cutoff_mbody,r_bonded_limit);
-
- if (comm->cutoff_mbody > comm->cellsize_limit)
- {
- comm->cellsize_limit = comm->cutoff_mbody;
- }
- }
- /* Without DLB and cutoff_mbody<cutoff, cutoff_mbody is dynamic */
- }
-
- if (debug)
- {
- fprintf(debug,"Bonded atom communication beyond the cut-off: %d\n"
- "cellsize limit %f\n",
- comm->bBondComm,comm->cellsize_limit);
- }
-
- if (MASTER(cr))
- {
- check_dd_restrictions(cr,dd,ir,fplog);
- }
-
- comm->partition_step = INT_MIN;
- dd->ddp_count = 0;
-
- clear_dd_cycle_counts(dd);
-
- return dd;
-}
-
-static void set_dlb_limits(gmx_domdec_t *dd)
-
-{
- int d;
-
- for(d=0; d<dd->ndim; d++)
- {
- dd->comm->cd[d].np = dd->comm->cd[d].np_dlb;
- dd->comm->cellsize_min[dd->dim[d]] =
- dd->comm->cellsize_min_dlb[dd->dim[d]];
- }
-}
-
-
-static void turn_on_dlb(FILE *fplog,t_commrec *cr,gmx_large_int_t step)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- real cellsize_min;
- int d,nc,i;
- char buf[STRLEN];
-
- dd = cr->dd;
- comm = dd->comm;
-
- if (fplog)
- {
- fprintf(fplog,"At step %s the performance loss due to force load imbalance is %.1f %%\n",gmx_step_str(step,buf),dd_force_imb_perf_loss(dd)*100);
- }
-
- cellsize_min = comm->cellsize_min[dd->dim[0]];
- for(d=1; d<dd->ndim; d++)
- {
- cellsize_min = min(cellsize_min,comm->cellsize_min[dd->dim[d]]);
- }
-
- if (cellsize_min < comm->cellsize_limit*1.05)
- {
- dd_warning(cr,fplog,"NOTE: the minimum cell size is smaller than 1.05 times the cell size limit, will not turn on dynamic load balancing\n");
-
- /* Change DLB from "auto" to "no". */
- comm->eDLB = edlbNO;
-
- return;
- }
-
- dd_warning(cr,fplog,"NOTE: Turning on dynamic load balancing\n");
- comm->bDynLoadBal = TRUE;
- dd->bGridJump = TRUE;
-
- set_dlb_limits(dd);
-
- /* We can set the required cell size info here,
- * so we do not need to communicate this.
- * The grid is completely uniform.
- */
- for(d=0; d<dd->ndim; d++)
- {
- if (comm->root[d])
- {
- comm->load[d].sum_m = comm->load[d].sum;
-
- nc = dd->nc[dd->dim[d]];
- for(i=0; i<nc; i++)
- {
- comm->root[d]->cell_f[i] = i/(real)nc;
- if (d > 0)
- {
- comm->root[d]->cell_f_max0[i] = i /(real)nc;
- comm->root[d]->cell_f_min1[i] = (i+1)/(real)nc;
- }
- }
- comm->root[d]->cell_f[nc] = 1.0;
- }
- }
-}
-
-static char *init_bLocalCG(gmx_mtop_t *mtop)
-{
- int ncg,cg;
- char *bLocalCG;
-
- ncg = ncg_mtop(mtop);
- snew(bLocalCG,ncg);
- for(cg=0; cg<ncg; cg++)
- {
- bLocalCG[cg] = FALSE;
- }
-
- return bLocalCG;
-}
-
-void dd_init_bondeds(FILE *fplog,
- gmx_domdec_t *dd,gmx_mtop_t *mtop,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- t_inputrec *ir,gmx_bool bBCheck,cginfo_mb_t *cginfo_mb)
-{
- gmx_domdec_comm_t *comm;
- gmx_bool bBondComm;
- int d;
-
- dd_make_reverse_top(fplog,dd,mtop,vsite,constr,ir,bBCheck);
-
- comm = dd->comm;
-
- if (comm->bBondComm)
- {
- /* Communicate atoms beyond the cut-off for bonded interactions */
- comm = dd->comm;
-
- comm->cglink = make_charge_group_links(mtop,dd,cginfo_mb);
-
- comm->bLocalCG = init_bLocalCG(mtop);
- }
- else
- {
- /* Only communicate atoms based on cut-off */
- comm->cglink = NULL;
- comm->bLocalCG = NULL;
- }
-}
-
-static void print_dd_settings(FILE *fplog,gmx_domdec_t *dd,
- t_inputrec *ir,
- gmx_bool bDynLoadBal,real dlb_scale,
- gmx_ddbox_t *ddbox)
-{
- gmx_domdec_comm_t *comm;
- int d;
- ivec np;
- real limit,shrink;
- char buf[64];
-
- if (fplog == NULL)
- {
- return;
- }
-
- comm = dd->comm;
-
- if (bDynLoadBal)
- {
- fprintf(fplog,"The maximum number of communication pulses is:");
- for(d=0; d<dd->ndim; d++)
- {
- fprintf(fplog," %c %d",dim2char(dd->dim[d]),comm->cd[d].np_dlb);
- }
- fprintf(fplog,"\n");
- fprintf(fplog,"The minimum size for domain decomposition cells is %.3f nm\n",comm->cellsize_limit);
- fprintf(fplog,"The requested allowed shrink of DD cells (option -dds) is: %.2f\n",dlb_scale);
- fprintf(fplog,"The allowed shrink of domain decomposition cells is:");
- for(d=0; d<DIM; d++)
- {
- if (dd->nc[d] > 1)
- {
- if (d >= ddbox->npbcdim && dd->nc[d] == 2)
- {
- shrink = 0;
- }
- else
- {
- shrink =
- comm->cellsize_min_dlb[d]/
- (ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
- }
- fprintf(fplog," %c %.2f",dim2char(d),shrink);
- }
- }
- fprintf(fplog,"\n");
- }
- else
- {
- set_dd_cell_sizes_slb(dd,ddbox,FALSE,np);
- fprintf(fplog,"The initial number of communication pulses is:");
- for(d=0; d<dd->ndim; d++)
- {
- fprintf(fplog," %c %d",dim2char(dd->dim[d]),np[dd->dim[d]]);
- }
- fprintf(fplog,"\n");
- fprintf(fplog,"The initial domain decomposition cell size is:");
- for(d=0; d<DIM; d++) {
- if (dd->nc[d] > 1)
- {
- fprintf(fplog," %c %.2f nm",
- dim2char(d),dd->comm->cellsize_min[d]);
- }
- }
- fprintf(fplog,"\n\n");
- }
-
- if (comm->bInterCGBondeds || dd->vsite_comm || dd->constraint_comm)
- {
- fprintf(fplog,"The maximum allowed distance for charge groups involved in interactions is:\n");
- fprintf(fplog,"%40s %-7s %6.3f nm\n",
- "non-bonded interactions","",comm->cutoff);
-
- if (bDynLoadBal)
- {
- limit = dd->comm->cellsize_limit;
- }
- else
- {
- if (dynamic_dd_box(ddbox,ir))
- {
- fprintf(fplog,"(the following are initial values, they could change due to box deformation)\n");
- }
- limit = dd->comm->cellsize_min[XX];
- for(d=1; d<DIM; d++)
- {
- limit = min(limit,dd->comm->cellsize_min[d]);
- }
- }
-
- if (comm->bInterCGBondeds)
- {
- fprintf(fplog,"%40s %-7s %6.3f nm\n",
- "two-body bonded interactions","(-rdd)",
- max(comm->cutoff,comm->cutoff_mbody));
- fprintf(fplog,"%40s %-7s %6.3f nm\n",
- "multi-body bonded interactions","(-rdd)",
- (comm->bBondComm || dd->bGridJump) ? comm->cutoff_mbody : min(comm->cutoff,limit));
- }
- if (dd->vsite_comm)
- {
- fprintf(fplog,"%40s %-7s %6.3f nm\n",
- "virtual site constructions","(-rcon)",limit);
- }
- if (dd->constraint_comm)
- {
- sprintf(buf,"atoms separated by up to %d constraints",
- 1+ir->nProjOrder);
- fprintf(fplog,"%40s %-7s %6.3f nm\n",
- buf,"(-rcon)",limit);
- }
- fprintf(fplog,"\n");
- }
-
- fflush(fplog);
-}
-
-void set_dd_parameters(FILE *fplog,gmx_domdec_t *dd,real dlb_scale,
- t_inputrec *ir,t_forcerec *fr,
- gmx_ddbox_t *ddbox)
-{
- gmx_domdec_comm_t *comm;
- int d,dim,npulse,npulse_d_max,npulse_d;
- gmx_bool bNoCutOff;
- int natoms_tot;
- real vol_frac;
-
- comm = dd->comm;
-
- bNoCutOff = (ir->rvdw == 0 || ir->rcoulomb == 0);
-
- if (EEL_PME(ir->coulombtype))
- {
- init_ddpme(dd,&comm->ddpme[0],0);
- if (comm->npmedecompdim >= 2)
- {
- init_ddpme(dd,&comm->ddpme[1],1);
- }
- }
- else
- {
- comm->npmenodes = 0;
- if (dd->pme_nodeid >= 0)
- {
- gmx_fatal_collective(FARGS,NULL,dd,
- "Can not have separate PME nodes without PME electrostatics");
- }
- }
-
- /* If each molecule is a single charge group
- * or we use domain decomposition for each periodic dimension,
- * we do not need to take pbc into account for the bonded interactions.
- */
- if (fr->ePBC == epbcNONE || !comm->bInterCGBondeds ||
- (dd->nc[XX]>1 && dd->nc[YY]>1 && (dd->nc[ZZ]>1 || fr->ePBC==epbcXY)))
- {
- fr->bMolPBC = FALSE;
- }
- else
- {
- fr->bMolPBC = TRUE;
- }
-
- if (debug)
- {
- fprintf(debug,"The DD cut-off is %f\n",comm->cutoff);
- }
- if (comm->eDLB != edlbNO)
- {
- /* Determine the maximum number of comm. pulses in one dimension */
-
- comm->cellsize_limit = max(comm->cellsize_limit,comm->cutoff_mbody);
-
- /* Determine the maximum required number of grid pulses */
- if (comm->cellsize_limit >= comm->cutoff)
- {
- /* Only a single pulse is required */
- npulse = 1;
- }
- else if (!bNoCutOff && comm->cellsize_limit > 0)
- {
- /* We round down slightly here to avoid overhead due to the latency
- * of extra communication calls when the cut-off
- * would be only slightly longer than the cell size.
- * Later cellsize_limit is redetermined,
- * so we can not miss interactions due to this rounding.
- */
- npulse = (int)(0.96 + comm->cutoff/comm->cellsize_limit);
- }
- else
- {
- /* There is no cell size limit */
- npulse = max(dd->nc[XX]-1,max(dd->nc[YY]-1,dd->nc[ZZ]-1));
- }
-
- if (!bNoCutOff && npulse > 1)
- {
- /* See if we can do with less pulses, based on dlb_scale */
- npulse_d_max = 0;
- for(d=0; d<dd->ndim; d++)
- {
- dim = dd->dim[d];
- npulse_d = (int)(1 + dd->nc[dim]*comm->cutoff
- /(ddbox->box_size[dim]*ddbox->skew_fac[dim]*dlb_scale));
- npulse_d_max = max(npulse_d_max,npulse_d);
- }
- npulse = min(npulse,npulse_d_max);
- }
-
- /* This env var can override npulse */
- d = dd_nst_env(fplog,"GMX_DD_NPULSE",0);
- if (d > 0)
- {
- npulse = d;
- }
-
- comm->maxpulse = 1;
- comm->bVacDLBNoLimit = (ir->ePBC == epbcNONE);
- for(d=0; d<dd->ndim; d++)
- {
- comm->cd[d].np_dlb = min(npulse,dd->nc[dd->dim[d]]-1);
- comm->cd[d].np_nalloc = comm->cd[d].np_dlb;
- snew(comm->cd[d].ind,comm->cd[d].np_nalloc);
- comm->maxpulse = max(comm->maxpulse,comm->cd[d].np_dlb);
- if (comm->cd[d].np_dlb < dd->nc[dd->dim[d]]-1)
- {
- comm->bVacDLBNoLimit = FALSE;
- }
- }
-
- /* cellsize_limit is set for LINCS in init_domain_decomposition */
- if (!comm->bVacDLBNoLimit)
- {
- comm->cellsize_limit = max(comm->cellsize_limit,
- comm->cutoff/comm->maxpulse);
- }
- comm->cellsize_limit = max(comm->cellsize_limit,comm->cutoff_mbody);
- /* Set the minimum cell size for each DD dimension */
- for(d=0; d<dd->ndim; d++)
- {
- if (comm->bVacDLBNoLimit ||
- comm->cd[d].np_dlb*comm->cellsize_limit >= comm->cutoff)
- {
- comm->cellsize_min_dlb[dd->dim[d]] = comm->cellsize_limit;
- }
- else
- {
- comm->cellsize_min_dlb[dd->dim[d]] =
- comm->cutoff/comm->cd[d].np_dlb;
- }
- }
- if (comm->cutoff_mbody <= 0)
- {
- comm->cutoff_mbody = min(comm->cutoff,comm->cellsize_limit);
- }
- if (comm->bDynLoadBal)
- {
- set_dlb_limits(dd);
- }
- }
-
- print_dd_settings(fplog,dd,ir,comm->bDynLoadBal,dlb_scale,ddbox);
- if (comm->eDLB == edlbAUTO)
- {
- if (fplog)
- {
- fprintf(fplog,"When dynamic load balancing gets turned on, these settings will change to:\n");
- }
- print_dd_settings(fplog,dd,ir,TRUE,dlb_scale,ddbox);
- }
-
- if (ir->ePBC == epbcNONE)
- {
- vol_frac = 1 - 1/(double)dd->nnodes;
- }
- else
- {
- vol_frac =
- (1 + comm_box_frac(dd->nc,comm->cutoff,ddbox))/(double)dd->nnodes;
- }
- if (debug)
- {
- fprintf(debug,"Volume fraction for all DD zones: %f\n",vol_frac);
- }
- natoms_tot = comm->cgs_gl.index[comm->cgs_gl.nr];
-
- dd->ga2la = ga2la_init(natoms_tot,vol_frac*natoms_tot);
-}
-
-static void merge_cg_buffers(int ncell,
- gmx_domdec_comm_dim_t *cd, int pulse,
- int *ncg_cell,
- int *index_gl, int *recv_i,
- rvec *cg_cm, rvec *recv_vr,
- int *cgindex,
- cginfo_mb_t *cginfo_mb,int *cginfo)
-{
- gmx_domdec_ind_t *ind,*ind_p;
- int p,cell,c,cg,cg0,cg1,cg_gl,nat;
- int shift,shift_at;
-
- ind = &cd->ind[pulse];
-
- /* First correct the already stored data */
- shift = ind->nrecv[ncell];
- for(cell=ncell-1; cell>=0; cell--)
- {
- shift -= ind->nrecv[cell];
- if (shift > 0)
- {
- /* Move the cg's present from previous grid pulses */
- cg0 = ncg_cell[ncell+cell];
- cg1 = ncg_cell[ncell+cell+1];
- cgindex[cg1+shift] = cgindex[cg1];
- for(cg=cg1-1; cg>=cg0; cg--)
- {
- index_gl[cg+shift] = index_gl[cg];
- copy_rvec(cg_cm[cg],cg_cm[cg+shift]);
- cgindex[cg+shift] = cgindex[cg];
- cginfo[cg+shift] = cginfo[cg];
- }
- /* Correct the already stored send indices for the shift */
- for(p=1; p<=pulse; p++)
- {
- ind_p = &cd->ind[p];
- cg0 = 0;
- for(c=0; c<cell; c++)
- {
- cg0 += ind_p->nsend[c];
- }
- cg1 = cg0 + ind_p->nsend[cell];
- for(cg=cg0; cg<cg1; cg++)
- {
- ind_p->index[cg] += shift;
- }
- }
- }
- }
-
- /* Merge in the communicated buffers */
- shift = 0;
- shift_at = 0;
- cg0 = 0;
- for(cell=0; cell<ncell; cell++)
- {
- cg1 = ncg_cell[ncell+cell+1] + shift;
- if (shift_at > 0)
- {
- /* Correct the old cg indices */
- for(cg=ncg_cell[ncell+cell]; cg<cg1; cg++)
- {
- cgindex[cg+1] += shift_at;
- }
- }
- for(cg=0; cg<ind->nrecv[cell]; cg++)
- {
- /* Copy this charge group from the buffer */
- index_gl[cg1] = recv_i[cg0];
- copy_rvec(recv_vr[cg0],cg_cm[cg1]);
- /* Add it to the cgindex */
- cg_gl = index_gl[cg1];
- cginfo[cg1] = ddcginfo(cginfo_mb,cg_gl);
- nat = GET_CGINFO_NATOMS(cginfo[cg1]);
- cgindex[cg1+1] = cgindex[cg1] + nat;
- cg0++;
- cg1++;
- shift_at += nat;
- }
- shift += ind->nrecv[cell];
- ncg_cell[ncell+cell+1] = cg1;
- }
-}
-
-static void make_cell2at_index(gmx_domdec_comm_dim_t *cd,
- int nzone,int cg0,const int *cgindex)
-{
- int cg,zone,p;
-
- /* Store the atom block boundaries for easy copying of communication buffers
- */
- cg = cg0;
- for(zone=0; zone<nzone; zone++)
- {
- for(p=0; p<cd->np; p++) {
- cd->ind[p].cell2at0[zone] = cgindex[cg];
- cg += cd->ind[p].nrecv[zone];
- cd->ind[p].cell2at1[zone] = cgindex[cg];
- }
- }
-}
-
-static gmx_bool missing_link(t_blocka *link,int cg_gl,char *bLocalCG)
-{
- int i;
- gmx_bool bMiss;
-
- bMiss = FALSE;
- for(i=link->index[cg_gl]; i<link->index[cg_gl+1]; i++)
- {
- if (!bLocalCG[link->a[i]])
- {
- bMiss = TRUE;
- }
- }
-
- return bMiss;
-}
-
-static void setup_dd_communication(gmx_domdec_t *dd,
- matrix box,gmx_ddbox_t *ddbox,t_forcerec *fr)
-{
- int dim_ind,dim,dim0,dim1=-1,dim2=-1,dimd,p,nat_tot;
- int nzone,nzone_send,zone,zonei,cg0,cg1;
- int c,i,j,cg,cg_gl,nrcg;
- int *zone_cg_range,pos_cg,*index_gl,*cgindex,*recv_i;
- gmx_domdec_comm_t *comm;
- gmx_domdec_zones_t *zones;
- gmx_domdec_comm_dim_t *cd;
- gmx_domdec_ind_t *ind;
- cginfo_mb_t *cginfo_mb;
- gmx_bool bBondComm,bDist2B,bDistMB,bDistMB_pulse,bDistBonded,bScrew;
- real r_mb,r_comm2,r_scomm2,r_bcomm2,r,r_0,r_1,r2,rb2,r2inc,inv_ncg,tric_sh;
- rvec rb,rn;
- real corner[DIM][4],corner_round_0=0,corner_round_1[4];
- real bcorner[DIM],bcorner_round_1=0;
- ivec tric_dist;
- rvec *cg_cm,*normal,*v_d,*v_0=NULL,*v_1=NULL,*recv_vr;
- real skew_fac2_d,skew_fac_01;
- rvec sf2_round;
- int nsend,nat;
-
- if (debug)
- {
- fprintf(debug,"Setting up DD communication\n");
- }
-
- comm = dd->comm;
- cg_cm = fr->cg_cm;
-
- for(dim_ind=0; dim_ind<dd->ndim; dim_ind++)
- {
- dim = dd->dim[dim_ind];
-
- /* Check if we need to use triclinic distances */
- tric_dist[dim_ind] = 0;
- for(i=0; i<=dim_ind; i++)
- {
- if (ddbox->tric_dir[dd->dim[i]])
- {
- tric_dist[dim_ind] = 1;
- }
- }
- }
-
- bBondComm = comm->bBondComm;
-
- /* Do we need to determine extra distances for multi-body bondeds? */
- bDistMB = (comm->bInterCGMultiBody && dd->bGridJump && dd->ndim > 1);
-
- /* Do we need to determine extra distances for only two-body bondeds? */
- bDist2B = (bBondComm && !bDistMB);
-
- r_comm2 = sqr(comm->cutoff);
- r_bcomm2 = sqr(comm->cutoff_mbody);
-
- if (debug)
- {
- fprintf(debug,"bBondComm %d, r_bc %f\n",bBondComm,sqrt(r_bcomm2));
- }
-
- zones = &comm->zones;
-
- dim0 = dd->dim[0];
- /* The first dimension is equal for all cells */
- corner[0][0] = comm->cell_x0[dim0];
- if (bDistMB)
- {
- bcorner[0] = corner[0][0];
- }
- if (dd->ndim >= 2)
- {
- dim1 = dd->dim[1];
- /* This cell row is only seen from the first row */
- corner[1][0] = comm->cell_x0[dim1];
- /* All rows can see this row */
- corner[1][1] = comm->cell_x0[dim1];
- if (dd->bGridJump)
- {
- corner[1][1] = max(comm->cell_x0[dim1],comm->zone_d1[1].mch0);
- if (bDistMB)
- {
- /* For the multi-body distance we need the maximum */
- bcorner[1] = max(comm->cell_x0[dim1],comm->zone_d1[1].p1_0);
- }
- }
- /* Set the upper-right corner for rounding */
- corner_round_0 = comm->cell_x1[dim0];
-
- if (dd->ndim >= 3)
- {
- dim2 = dd->dim[2];
- for(j=0; j<4; j++)
- {
- corner[2][j] = comm->cell_x0[dim2];
- }
- if (dd->bGridJump)
- {
- /* Use the maximum of the i-cells that see a j-cell */
- for(i=0; i<zones->nizone; i++)
- {
- for(j=zones->izone[i].j0; j<zones->izone[i].j1; j++)
- {
- if (j >= 4)
- {
- corner[2][j-4] =
- max(corner[2][j-4],
- comm->zone_d2[zones->shift[i][dim0]][zones->shift[i][dim1]].mch0);
- }
- }
- }
- if (bDistMB)
- {
- /* For the multi-body distance we need the maximum */
- bcorner[2] = comm->cell_x0[dim2];
- for(i=0; i<2; i++)
- {
- for(j=0; j<2; j++)
- {
- bcorner[2] = max(bcorner[2],
- comm->zone_d2[i][j].p1_0);
- }
- }
- }
- }
-
- /* Set the upper-right corner for rounding */
- /* Cell (0,0,0) and cell (1,0,0) can see cell 4 (0,1,1)
- * Only cell (0,0,0) can see cell 7 (1,1,1)
- */
- corner_round_1[0] = comm->cell_x1[dim1];
- corner_round_1[3] = comm->cell_x1[dim1];
- if (dd->bGridJump)
- {
- corner_round_1[0] = max(comm->cell_x1[dim1],
- comm->zone_d1[1].mch1);
- if (bDistMB)
- {
- /* For the multi-body distance we need the maximum */
- bcorner_round_1 = max(comm->cell_x1[dim1],
- comm->zone_d1[1].p1_1);
- }
- }
- }
- }
-
- /* Triclinic stuff */
- normal = ddbox->normal;
- skew_fac_01 = 0;
- if (dd->ndim >= 2)
- {
- v_0 = ddbox->v[dim0];
- if (ddbox->tric_dir[dim0] && ddbox->tric_dir[dim1])
- {
- /* Determine the coupling coefficient for the distances
- * to the cell planes along dim0 and dim1 through dim2.
- * This is required for correct rounding.
- */
- skew_fac_01 =
- ddbox->v[dim0][dim1+1][dim0]*ddbox->v[dim1][dim1+1][dim1];
- if (debug)
- {
- fprintf(debug,"\nskew_fac_01 %f\n",skew_fac_01);
- }
- }
- }
- if (dd->ndim >= 3)
- {
- v_1 = ddbox->v[dim1];
- }
-
- zone_cg_range = zones->cg_range;
- index_gl = dd->index_gl;
- cgindex = dd->cgindex;
- cginfo_mb = fr->cginfo_mb;
-
- zone_cg_range[0] = 0;
- zone_cg_range[1] = dd->ncg_home;
- comm->zone_ncg1[0] = dd->ncg_home;
- pos_cg = dd->ncg_home;
-
- nat_tot = dd->nat_home;
- nzone = 1;
- for(dim_ind=0; dim_ind<dd->ndim; dim_ind++)
- {
- dim = dd->dim[dim_ind];
- cd = &comm->cd[dim_ind];
-
- if (dim >= ddbox->npbcdim && dd->ci[dim] == 0)
- {
- /* No pbc in this dimension, the first node should not comm. */
- nzone_send = 0;
- }
- else
- {
- nzone_send = nzone;
- }
-
- bScrew = (dd->bScrewPBC && dim == XX);
-
- v_d = ddbox->v[dim];
- skew_fac2_d = sqr(ddbox->skew_fac[dim]);
-
- cd->bInPlace = TRUE;
- for(p=0; p<cd->np; p++)
- {
- /* Only atoms communicated in the first pulse are used
- * for multi-body bonded interactions or for bBondComm.
- */
- bDistBonded = ((bDistMB || bDist2B) && p == 0);
- bDistMB_pulse = (bDistMB && bDistBonded);
-
- ind = &cd->ind[p];
- nsend = 0;
- nat = 0;
- for(zone=0; zone<nzone_send; zone++)
- {
- if (tric_dist[dim_ind] && dim_ind > 0)
- {
- /* Determine slightly more optimized skew_fac's
- * for rounding.
- * This reduces the number of communicated atoms
- * by about 10% for 3D DD of rhombic dodecahedra.
- */
- for(dimd=0; dimd<dim; dimd++)
- {
- sf2_round[dimd] = 1;
- if (ddbox->tric_dir[dimd])
- {
- for(i=dd->dim[dimd]+1; i<DIM; i++)
- {
- /* If we are shifted in dimension i
- * and the cell plane is tilted forward
- * in dimension i, skip this coupling.
- */
- if (!(zones->shift[nzone+zone][i] &&
- ddbox->v[dimd][i][dimd] >= 0))
- {
- sf2_round[dimd] +=
- sqr(ddbox->v[dimd][i][dimd]);
- }
- }
- sf2_round[dimd] = 1/sf2_round[dimd];
- }
- }
- }
-
- zonei = zone_perm[dim_ind][zone];
- if (p == 0)
- {
- /* Here we permutate the zones to obtain a convenient order
- * for neighbor searching
- */
- cg0 = zone_cg_range[zonei];
- cg1 = zone_cg_range[zonei+1];
- }
- else
- {
- /* Look only at the cg's received in the previous grid pulse
- */
- cg1 = zone_cg_range[nzone+zone+1];
- cg0 = cg1 - cd->ind[p-1].nrecv[zone];
- }
- ind->nsend[zone] = 0;
- for(cg=cg0; cg<cg1; cg++)
- {
- r2 = 0;
- rb2 = 0;
- if (tric_dist[dim_ind] == 0)
- {
- /* Rectangular direction, easy */
- r = cg_cm[cg][dim] - corner[dim_ind][zone];
- if (r > 0)
- {
- r2 += r*r;
- }
- if (bDistMB_pulse)
- {
- r = cg_cm[cg][dim] - bcorner[dim_ind];
- if (r > 0)
- {
- rb2 += r*r;
- }
- }
- /* Rounding gives at most a 16% reduction
- * in communicated atoms
- */
- if (dim_ind >= 1 && (zonei == 1 || zonei == 2))
- {
- r = cg_cm[cg][dim0] - corner_round_0;
- /* This is the first dimension, so always r >= 0 */
- r2 += r*r;
- if (bDistMB_pulse)
- {
- rb2 += r*r;
- }
- }
- if (dim_ind == 2 && (zonei == 2 || zonei == 3))
- {
- r = cg_cm[cg][dim1] - corner_round_1[zone];
- if (r > 0)
- {
- r2 += r*r;
- }
- if (bDistMB_pulse)
- {
- r = cg_cm[cg][dim1] - bcorner_round_1;
- if (r > 0)
- {
- rb2 += r*r;
- }
- }
- }
- }
- else
- {
- /* Triclinic direction, more complicated */
- clear_rvec(rn);
- clear_rvec(rb);
- /* Rounding, conservative as the skew_fac multiplication
- * will slightly underestimate the distance.
- */
- if (dim_ind >= 1 && (zonei == 1 || zonei == 2))
- {
- rn[dim0] = cg_cm[cg][dim0] - corner_round_0;
- for(i=dim0+1; i<DIM; i++)
- {
- rn[dim0] -= cg_cm[cg][i]*v_0[i][dim0];
- }
- r2 = rn[dim0]*rn[dim0]*sf2_round[dim0];
- if (bDistMB_pulse)
- {
- rb[dim0] = rn[dim0];
- rb2 = r2;
- }
- /* Take care that the cell planes along dim0 might not
- * be orthogonal to those along dim1 and dim2.
- */
- for(i=1; i<=dim_ind; i++)
- {
- dimd = dd->dim[i];
- if (normal[dim0][dimd] > 0)
- {
- rn[dimd] -= rn[dim0]*normal[dim0][dimd];
- if (bDistMB_pulse)
- {
- rb[dimd] -= rb[dim0]*normal[dim0][dimd];
- }
- }
- }
- }
- if (dim_ind == 2 && (zonei == 2 || zonei == 3))
- {
- rn[dim1] += cg_cm[cg][dim1] - corner_round_1[zone];
- tric_sh = 0;
- for(i=dim1+1; i<DIM; i++)
- {
- tric_sh -= cg_cm[cg][i]*v_1[i][dim1];
- }
- rn[dim1] += tric_sh;
- if (rn[dim1] > 0)
- {
- r2 += rn[dim1]*rn[dim1]*sf2_round[dim1];
- /* Take care of coupling of the distances
- * to the planes along dim0 and dim1 through dim2.
- */
- r2 -= rn[dim0]*rn[dim1]*skew_fac_01;
- /* Take care that the cell planes along dim1
- * might not be orthogonal to that along dim2.
- */
- if (normal[dim1][dim2] > 0)
- {
- rn[dim2] -= rn[dim1]*normal[dim1][dim2];
- }
- }
- if (bDistMB_pulse)
- {
- rb[dim1] +=
- cg_cm[cg][dim1] - bcorner_round_1 + tric_sh;
- if (rb[dim1] > 0)
- {
- rb2 += rb[dim1]*rb[dim1]*sf2_round[dim1];
- /* Take care of coupling of the distances
- * to the planes along dim0 and dim1 through dim2.
- */
- rb2 -= rb[dim0]*rb[dim1]*skew_fac_01;
- /* Take care that the cell planes along dim1
- * might not be orthogonal to that along dim2.
- */
- if (normal[dim1][dim2] > 0)
- {
- rb[dim2] -= rb[dim1]*normal[dim1][dim2];
- }
- }
- }
- }
- /* The distance along the communication direction */
- rn[dim] += cg_cm[cg][dim] - corner[dim_ind][zone];
- tric_sh = 0;
- for(i=dim+1; i<DIM; i++)
- {
- tric_sh -= cg_cm[cg][i]*v_d[i][dim];
- }
- rn[dim] += tric_sh;
- if (rn[dim] > 0)
- {
- r2 += rn[dim]*rn[dim]*skew_fac2_d;
- /* Take care of coupling of the distances
- * to the planes along dim0 and dim1 through dim2.
- */
- if (dim_ind == 1 && zonei == 1)
- {
- r2 -= rn[dim0]*rn[dim]*skew_fac_01;
- }
- }
- if (bDistMB_pulse)
- {
- clear_rvec(rb);
- rb[dim] += cg_cm[cg][dim] - bcorner[dim_ind] + tric_sh;
- if (rb[dim] > 0)
- {
- rb2 += rb[dim]*rb[dim]*skew_fac2_d;
- /* Take care of coupling of the distances
- * to the planes along dim0 and dim1 through dim2.
- */
- if (dim_ind == 1 && zonei == 1)
- {
- rb2 -= rb[dim0]*rb[dim]*skew_fac_01;
- }
- }
- }
- }
-
- if (r2 < r_comm2 ||
- (bDistBonded &&
- ((bDistMB && rb2 < r_bcomm2) ||
- (bDist2B && r2 < r_bcomm2)) &&
- (!bBondComm ||
- (GET_CGINFO_BOND_INTER(fr->cginfo[cg]) &&
- missing_link(comm->cglink,index_gl[cg],
- comm->bLocalCG)))))
- {
- /* Make an index to the local charge groups */
- if (nsend+1 > ind->nalloc)
- {
- ind->nalloc = over_alloc_large(nsend+1);
- srenew(ind->index,ind->nalloc);
- }
- if (nsend+1 > comm->nalloc_int)
- {
- comm->nalloc_int = over_alloc_large(nsend+1);
- srenew(comm->buf_int,comm->nalloc_int);
- }
- ind->index[nsend] = cg;
- comm->buf_int[nsend] = index_gl[cg];
- ind->nsend[zone]++;
- vec_rvec_check_alloc(&comm->vbuf,nsend+1);
-
- if (dd->ci[dim] == 0)
- {
- /* Correct cg_cm for pbc */
- rvec_add(cg_cm[cg],box[dim],comm->vbuf.v[nsend]);
- if (bScrew)
- {
- comm->vbuf.v[nsend][YY] =
- box[YY][YY]-comm->vbuf.v[nsend][YY];
- comm->vbuf.v[nsend][ZZ] =
- box[ZZ][ZZ]-comm->vbuf.v[nsend][ZZ];
- }
- }
- else
- {
- copy_rvec(cg_cm[cg],comm->vbuf.v[nsend]);
- }
- nsend++;
- nat += cgindex[cg+1] - cgindex[cg];
- }
- }
- }
- /* Clear the counts in case we do not have pbc */
- for(zone=nzone_send; zone<nzone; zone++)
- {
- ind->nsend[zone] = 0;
- }
- ind->nsend[nzone] = nsend;
- ind->nsend[nzone+1] = nat;
- /* Communicate the number of cg's and atoms to receive */
- dd_sendrecv_int(dd, dim_ind, dddirBackward,
- ind->nsend, nzone+2,
- ind->nrecv, nzone+2);
-
- /* The rvec buffer is also required for atom buffers of size nsend
- * in dd_move_x and dd_move_f.
- */
- vec_rvec_check_alloc(&comm->vbuf,ind->nsend[nzone+1]);
-
- if (p > 0)
- {
- /* We can receive in place if only the last zone is not empty */
- for(zone=0; zone<nzone-1; zone++)
- {
- if (ind->nrecv[zone] > 0)
- {
- cd->bInPlace = FALSE;
- }
- }
- if (!cd->bInPlace)
- {
- /* The int buffer is only required here for the cg indices */
- if (ind->nrecv[nzone] > comm->nalloc_int2)
- {
- comm->nalloc_int2 = over_alloc_dd(ind->nrecv[nzone]);
- srenew(comm->buf_int2,comm->nalloc_int2);
- }
- /* The rvec buffer is also required for atom buffers
- * of size nrecv in dd_move_x and dd_move_f.
- */
- i = max(cd->ind[0].nrecv[nzone+1],ind->nrecv[nzone+1]);
- vec_rvec_check_alloc(&comm->vbuf2,i);
- }
- }
-
- /* Make space for the global cg indices */
- if (pos_cg + ind->nrecv[nzone] > dd->cg_nalloc
- || dd->cg_nalloc == 0)
- {
- dd->cg_nalloc = over_alloc_dd(pos_cg + ind->nrecv[nzone]);
- srenew(index_gl,dd->cg_nalloc);
- srenew(cgindex,dd->cg_nalloc+1);
- }
- /* Communicate the global cg indices */
- if (cd->bInPlace)
- {
- recv_i = index_gl + pos_cg;
- }
- else
- {
- recv_i = comm->buf_int2;
- }
- dd_sendrecv_int(dd, dim_ind, dddirBackward,
- comm->buf_int, nsend,
- recv_i, ind->nrecv[nzone]);
-
- /* Make space for cg_cm */
- if (pos_cg + ind->nrecv[nzone] > fr->cg_nalloc)
- {
- dd_realloc_fr_cg(fr,pos_cg + ind->nrecv[nzone]);
- cg_cm = fr->cg_cm;
- }
- /* Communicate cg_cm */
- if (cd->bInPlace)
- {
- recv_vr = cg_cm + pos_cg;
- }
- else
- {
- recv_vr = comm->vbuf2.v;
- }
- dd_sendrecv_rvec(dd, dim_ind, dddirBackward,
- comm->vbuf.v, nsend,
- recv_vr, ind->nrecv[nzone]);
-
- /* Make the charge group index */
- if (cd->bInPlace)
- {
- zone = (p == 0 ? 0 : nzone - 1);
- while (zone < nzone)
- {
- for(cg=0; cg<ind->nrecv[zone]; cg++)
- {
- cg_gl = index_gl[pos_cg];
- fr->cginfo[pos_cg] = ddcginfo(cginfo_mb,cg_gl);
- nrcg = GET_CGINFO_NATOMS(fr->cginfo[pos_cg]);
- cgindex[pos_cg+1] = cgindex[pos_cg] + nrcg;
- if (bBondComm)
- {
- /* Update the charge group presence,
- * so we can use it in the next pass of the loop.
- */
- comm->bLocalCG[cg_gl] = TRUE;
- }
- pos_cg++;
- }
- if (p == 0)
- {
- comm->zone_ncg1[nzone+zone] = ind->nrecv[zone];
- }
- zone++;
- zone_cg_range[nzone+zone] = pos_cg;
- }
- }
- else
- {
- /* This part of the code is never executed with bBondComm. */
- merge_cg_buffers(nzone,cd,p,zone_cg_range,
- index_gl,recv_i,cg_cm,recv_vr,
- cgindex,fr->cginfo_mb,fr->cginfo);
- pos_cg += ind->nrecv[nzone];
- }
- nat_tot += ind->nrecv[nzone+1];
- }
- if (!cd->bInPlace)
- {
- /* Store the atom block for easy copying of communication buffers */
- make_cell2at_index(cd,nzone,zone_cg_range[nzone],cgindex);
- }
- nzone += nzone;
- }
- dd->index_gl = index_gl;
- dd->cgindex = cgindex;
-
- dd->ncg_tot = zone_cg_range[zones->n];
- dd->nat_tot = nat_tot;
- comm->nat[ddnatHOME] = dd->nat_home;
- for(i=ddnatZONE; i<ddnatNR; i++)
- {
- comm->nat[i] = dd->nat_tot;
- }
-
- if (!bBondComm)
- {
- /* We don't need to update cginfo, since that was alrady done above.
- * So we pass NULL for the forcerec.
- */
- dd_set_cginfo(dd->index_gl,dd->ncg_home,dd->ncg_tot,
- NULL,comm->bLocalCG);
- }
-
- if (debug)
- {
- fprintf(debug,"Finished setting up DD communication, zones:");
- for(c=0; c<zones->n; c++)
- {
- fprintf(debug," %d",zones->cg_range[c+1]-zones->cg_range[c]);
- }
- fprintf(debug,"\n");
- }
-}
-
-static void set_cg_boundaries(gmx_domdec_zones_t *zones)
-{
- int c;
-
- for(c=0; c<zones->nizone; c++)
- {
- zones->izone[c].cg1 = zones->cg_range[c+1];
- zones->izone[c].jcg0 = zones->cg_range[zones->izone[c].j0];
- zones->izone[c].jcg1 = zones->cg_range[zones->izone[c].j1];
- }
-}
-
-static int comp_cgsort(const void *a,const void *b)
-{
- int comp;
-
- gmx_cgsort_t *cga,*cgb;
- cga = (gmx_cgsort_t *)a;
- cgb = (gmx_cgsort_t *)b;
-
- comp = cga->nsc - cgb->nsc;
- if (comp == 0)
- {
- comp = cga->ind_gl - cgb->ind_gl;
- }
-
- return comp;
-}
-
-static void order_int_cg(int n,gmx_cgsort_t *sort,
- int *a,int *buf)
-{
- int i;
-
- /* Order the data */
- for(i=0; i<n; i++)
- {
- buf[i] = a[sort[i].ind];
- }
-
- /* Copy back to the original array */
- for(i=0; i<n; i++)
- {
- a[i] = buf[i];
- }
-}
-
-static void order_vec_cg(int n,gmx_cgsort_t *sort,
- rvec *v,rvec *buf)
-{
- int i;
-
- /* Order the data */
- for(i=0; i<n; i++)
- {
- copy_rvec(v[sort[i].ind],buf[i]);
- }
-
- /* Copy back to the original array */
- for(i=0; i<n; i++)
- {
- copy_rvec(buf[i],v[i]);
- }
-}
-
-static void order_vec_atom(int ncg,int *cgindex,gmx_cgsort_t *sort,
- rvec *v,rvec *buf)
-{
- int a,atot,cg,cg0,cg1,i;
-
- /* Order the data */
- a = 0;
- for(cg=0; cg<ncg; cg++)
- {
- cg0 = cgindex[sort[cg].ind];
- cg1 = cgindex[sort[cg].ind+1];
- for(i=cg0; i<cg1; i++)
- {
- copy_rvec(v[i],buf[a]);
- a++;
- }
- }
- atot = a;
-
- /* Copy back to the original array */
- for(a=0; a<atot; a++)
- {
- copy_rvec(buf[a],v[a]);
- }
-}
-
-static void ordered_sort(int nsort2,gmx_cgsort_t *sort2,
- int nsort_new,gmx_cgsort_t *sort_new,
- gmx_cgsort_t *sort1)
-{
- int i1,i2,i_new;
-
- /* The new indices are not very ordered, so we qsort them */
- qsort_threadsafe(sort_new,nsort_new,sizeof(sort_new[0]),comp_cgsort);
-
- /* sort2 is already ordered, so now we can merge the two arrays */
- i1 = 0;
- i2 = 0;
- i_new = 0;
- while(i2 < nsort2 || i_new < nsort_new)
- {
- if (i2 == nsort2)
- {
- sort1[i1++] = sort_new[i_new++];
- }
- else if (i_new == nsort_new)
- {
- sort1[i1++] = sort2[i2++];
- }
- else if (sort2[i2].nsc < sort_new[i_new].nsc ||
- (sort2[i2].nsc == sort_new[i_new].nsc &&
- sort2[i2].ind_gl < sort_new[i_new].ind_gl))
- {
- sort1[i1++] = sort2[i2++];
- }
- else
- {
- sort1[i1++] = sort_new[i_new++];
- }
- }
-}
-
-static void dd_sort_state(gmx_domdec_t *dd,int ePBC,
- rvec *cgcm,t_forcerec *fr,t_state *state,
- int ncg_home_old)
-{
- gmx_domdec_sort_t *sort;
- gmx_cgsort_t *cgsort,*sort_i;
- int ncg_new,nsort2,nsort_new,i,cell_index,*ibuf,cgsize;
- rvec *vbuf;
-
- sort = dd->comm->sort;
-
- if (dd->ncg_home > sort->sort_nalloc)
- {
- sort->sort_nalloc = over_alloc_dd(dd->ncg_home);
- srenew(sort->sort1,sort->sort_nalloc);
- srenew(sort->sort2,sort->sort_nalloc);
- }
-
- if (ncg_home_old >= 0)
- {
- /* The charge groups that remained in the same ns grid cell
- * are completely ordered. So we can sort efficiently by sorting
- * the charge groups that did move into the stationary list.
- */
- ncg_new = 0;
- nsort2 = 0;
- nsort_new = 0;
- for(i=0; i<dd->ncg_home; i++)
- {
- /* Check if this cg did not move to another node */
- cell_index = fr->ns.grid->cell_index[i];
- if (cell_index != 4*fr->ns.grid->ncells)
- {
- if (i >= ncg_home_old || cell_index != sort->sort1[i].nsc)
- {
- /* This cg is new on this node or moved ns grid cell */
- if (nsort_new >= sort->sort_new_nalloc)
- {
- sort->sort_new_nalloc = over_alloc_dd(nsort_new+1);
- srenew(sort->sort_new,sort->sort_new_nalloc);
- }
- sort_i = &(sort->sort_new[nsort_new++]);
- }
- else
- {
- /* This cg did not move */
- sort_i = &(sort->sort2[nsort2++]);
- }
- /* Sort on the ns grid cell indices
- * and the global topology index
- */
- sort_i->nsc = cell_index;
- sort_i->ind_gl = dd->index_gl[i];
- sort_i->ind = i;
- ncg_new++;
- }
- }
- if (debug)
- {
- fprintf(debug,"ordered sort cgs: stationary %d moved %d\n",
- nsort2,nsort_new);
- }
- /* Sort efficiently */
- ordered_sort(nsort2,sort->sort2,nsort_new,sort->sort_new,sort->sort1);
- }
- else
- {
- cgsort = sort->sort1;
- ncg_new = 0;
- for(i=0; i<dd->ncg_home; i++)
- {
- /* Sort on the ns grid cell indices
- * and the global topology index
- */
- cgsort[i].nsc = fr->ns.grid->cell_index[i];
- cgsort[i].ind_gl = dd->index_gl[i];
- cgsort[i].ind = i;
- if (cgsort[i].nsc != 4*fr->ns.grid->ncells)
- {
- ncg_new++;
- }
- }
- if (debug)
- {
- fprintf(debug,"qsort cgs: %d new home %d\n",dd->ncg_home,ncg_new);
- }
- /* Determine the order of the charge groups using qsort */
- qsort_threadsafe(cgsort,dd->ncg_home,sizeof(cgsort[0]),comp_cgsort);
- }
- cgsort = sort->sort1;
-
- /* We alloc with the old size, since cgindex is still old */
- vec_rvec_check_alloc(&dd->comm->vbuf,dd->cgindex[dd->ncg_home]);
- vbuf = dd->comm->vbuf.v;
-
- /* Remove the charge groups which are no longer at home here */
- dd->ncg_home = ncg_new;
-
- /* Reorder the state */
- for(i=0; i<estNR; i++)
- {
- if (EST_DISTR(i) && state->flags & (1<<i))
- {
- switch (i)
- {
- case estX:
- order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->x,vbuf);
- break;
- case estV:
- order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->v,vbuf);
- break;
- case estSDX:
- order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->sd_X,vbuf);
- break;
- case estCGP:
- order_vec_atom(dd->ncg_home,dd->cgindex,cgsort,state->cg_p,vbuf);
- break;
- case estLD_RNG:
- case estLD_RNGI:
- case estDISRE_INITF:
- case estDISRE_RM3TAV:
- case estORIRE_INITF:
- case estORIRE_DTAV:
- /* No ordering required */
- break;
- default:
- gmx_incons("Unknown state entry encountered in dd_sort_state");
- break;
- }
- }
- }
- /* Reorder cgcm */
- order_vec_cg(dd->ncg_home,cgsort,cgcm,vbuf);
-
- if (dd->ncg_home+1 > sort->ibuf_nalloc)
- {
- sort->ibuf_nalloc = over_alloc_dd(dd->ncg_home+1);
- srenew(sort->ibuf,sort->ibuf_nalloc);
- }
- ibuf = sort->ibuf;
- /* Reorder the global cg index */
- order_int_cg(dd->ncg_home,cgsort,dd->index_gl,ibuf);
- /* Reorder the cginfo */
- order_int_cg(dd->ncg_home,cgsort,fr->cginfo,ibuf);
- /* Rebuild the local cg index */
- ibuf[0] = 0;
- for(i=0; i<dd->ncg_home; i++)
- {
- cgsize = dd->cgindex[cgsort[i].ind+1] - dd->cgindex[cgsort[i].ind];
- ibuf[i+1] = ibuf[i] + cgsize;
- }
- for(i=0; i<dd->ncg_home+1; i++)
- {
- dd->cgindex[i] = ibuf[i];
- }
- /* Set the home atom number */
- dd->nat_home = dd->cgindex[dd->ncg_home];
-
- /* Copy the sorted ns cell indices back to the ns grid struct */
- for(i=0; i<dd->ncg_home; i++)
- {
- fr->ns.grid->cell_index[i] = cgsort[i].nsc;
- }
- fr->ns.grid->nr = dd->ncg_home;
-}
-
-static void add_dd_statistics(gmx_domdec_t *dd)
-{
- gmx_domdec_comm_t *comm;
- int ddnat;
-
- comm = dd->comm;
-
- for(ddnat=ddnatZONE; ddnat<ddnatNR; ddnat++)
- {
- comm->sum_nat[ddnat-ddnatZONE] +=
- comm->nat[ddnat] - comm->nat[ddnat-1];
- }
- comm->ndecomp++;
-}
-
-void reset_dd_statistics_counters(gmx_domdec_t *dd)
-{
- gmx_domdec_comm_t *comm;
- int ddnat;
-
- comm = dd->comm;
-
- /* Reset all the statistics and counters for total run counting */
- for(ddnat=ddnatZONE; ddnat<ddnatNR; ddnat++)
- {
- comm->sum_nat[ddnat-ddnatZONE] = 0;
- }
- comm->ndecomp = 0;
- comm->nload = 0;
- comm->load_step = 0;
- comm->load_sum = 0;
- comm->load_max = 0;
- clear_ivec(comm->load_lim);
- comm->load_mdf = 0;
- comm->load_pme = 0;
-}
-
-void print_dd_statistics(t_commrec *cr,t_inputrec *ir,FILE *fplog)
-{
- gmx_domdec_comm_t *comm;
- int ddnat;
- double av;
-
- comm = cr->dd->comm;
-
- gmx_sumd(ddnatNR-ddnatZONE,comm->sum_nat,cr);
-
- if (fplog == NULL)
- {
- return;
- }
-
- fprintf(fplog,"\n D O M A I N D E C O M P O S I T I O N S T A T I S T I C S\n\n");
-
- for(ddnat=ddnatZONE; ddnat<ddnatNR; ddnat++)
- {
- av = comm->sum_nat[ddnat-ddnatZONE]/comm->ndecomp;
- switch(ddnat)
- {
- case ddnatZONE:
- fprintf(fplog,
- " av. #atoms communicated per step for force: %d x %.1f\n",
- 2,av);
- break;
- case ddnatVSITE:
- if (cr->dd->vsite_comm)
- {
- fprintf(fplog,
- " av. #atoms communicated per step for vsites: %d x %.1f\n",
- (EEL_PME(ir->coulombtype) || ir->coulombtype==eelEWALD) ? 3 : 2,
- av);
- }
- break;
- case ddnatCON:
- if (cr->dd->constraint_comm)
- {
- fprintf(fplog,
- " av. #atoms communicated per step for LINCS: %d x %.1f\n",
- 1 + ir->nLincsIter,av);
- }
- break;
- default:
- gmx_incons(" Unknown type for DD statistics");
- }
- }
- fprintf(fplog,"\n");
-
- if (comm->bRecordLoad && EI_DYNAMICS(ir->eI))
- {
- print_dd_load_av(fplog,cr->dd);
- }
-}
-
-void dd_partition_system(FILE *fplog,
- gmx_large_int_t step,
- t_commrec *cr,
- gmx_bool bMasterState,
- int nstglobalcomm,
- t_state *state_global,
- gmx_mtop_t *top_global,
- t_inputrec *ir,
- t_state *state_local,
- rvec **f,
- t_mdatoms *mdatoms,
- gmx_localtop_t *top_local,
- t_forcerec *fr,
- gmx_vsite_t *vsite,
- gmx_shellfc_t shellfc,
- gmx_constr_t constr,
- t_nrnb *nrnb,
- gmx_wallcycle_t wcycle,
- gmx_bool bVerbose)
-{
- gmx_domdec_t *dd;
- gmx_domdec_comm_t *comm;
- gmx_ddbox_t ddbox={0};
- t_block *cgs_gl;
- gmx_large_int_t step_pcoupl;
- rvec cell_ns_x0,cell_ns_x1;
- int i,j,n,cg0=0,ncg_home_old=-1,nat_f_novirsum;
- gmx_bool bBoxChanged,bNStGlobalComm,bDoDLB,bCheckDLB,bTurnOnDLB,bLogLoad;
- gmx_bool bRedist,bSortCG,bResortAll;
- ivec ncells_old,np;
- real grid_density;
- char sbuf[22];
-
- dd = cr->dd;
- comm = dd->comm;
-
- bBoxChanged = (bMasterState || DEFORM(*ir));
- if (ir->epc != epcNO)
- {
- /* With nstcalcenery > 1 pressure coupling happens.
- * one step after calculating the energies.
- * Box scaling happens at the end of the MD step,
- * after the DD partitioning.
- * We therefore have to do DLB in the first partitioning
- * after an MD step where P-coupling occured.
- * We need to determine the last step in which p-coupling occurred.
- * MRS -- need to validate this for vv?
- */
- n = ir->nstcalcenergy;
- if (n == 1)
- {
- step_pcoupl = step - 1;
- }
- else
- {
- step_pcoupl = ((step - 1)/n)*n + 1;
- }
- if (step_pcoupl >= comm->partition_step)
- {
- bBoxChanged = TRUE;
- }
- }
-
- bNStGlobalComm = (step >= comm->partition_step + nstglobalcomm);
-
- if (!comm->bDynLoadBal)
- {
- bDoDLB = FALSE;
- }
- else
- {
- /* Should we do dynamic load balacing this step?
- * Since it requires (possibly expensive) global communication,
- * we might want to do DLB less frequently.
- */
- if (bBoxChanged || ir->epc != epcNO)
- {
- bDoDLB = bBoxChanged;
- }
- else
- {
- bDoDLB = bNStGlobalComm;
- }
- }
-
- /* Check if we have recorded loads on the nodes */
- if (comm->bRecordLoad && dd_load_count(comm))
- {
- if (comm->eDLB == edlbAUTO && !comm->bDynLoadBal)
- {
- /* Check if we should use DLB at the second partitioning
- * and every 100 partitionings,
- * so the extra communication cost is negligible.
- */
- n = max(100,nstglobalcomm);
- bCheckDLB = (comm->n_load_collect == 0 ||
- comm->n_load_have % n == n-1);
- }
- else
- {
- bCheckDLB = FALSE;
- }
-
- /* Print load every nstlog, first and last step to the log file */
- bLogLoad = ((ir->nstlog > 0 && step % ir->nstlog == 0) ||
- comm->n_load_collect == 0 ||
- (step + ir->nstlist > ir->init_step + ir->nsteps));
-
- /* Avoid extra communication due to verbose screen output
- * when nstglobalcomm is set.
- */
- if (bDoDLB || bLogLoad || bCheckDLB ||
- (bVerbose && (ir->nstlist == 0 || nstglobalcomm <= ir->nstlist)))
- {
- get_load_distribution(dd,wcycle);
- if (DDMASTER(dd))
- {
- if (bLogLoad)
- {
- dd_print_load(fplog,dd,step-1);
- }
- if (bVerbose)
- {
- dd_print_load_verbose(dd);
- }
- }
- comm->n_load_collect++;
-
- if (bCheckDLB) {
- /* Since the timings are node dependent, the master decides */
- if (DDMASTER(dd))
- {
- bTurnOnDLB =
- (dd_force_imb_perf_loss(dd) >= DD_PERF_LOSS);
- if (debug)
- {
- fprintf(debug,"step %s, imb loss %f\n",
- gmx_step_str(step,sbuf),
- dd_force_imb_perf_loss(dd));
- }
- }
- dd_bcast(dd,sizeof(bTurnOnDLB),&bTurnOnDLB);
- if (bTurnOnDLB)
- {
- turn_on_dlb(fplog,cr,step);
- bDoDLB = TRUE;
- }
- }
- }
- comm->n_load_have++;
- }
-
- cgs_gl = &comm->cgs_gl;
-
- bRedist = FALSE;
- if (bMasterState)
- {
- /* Clear the old state */
- clear_dd_indices(dd,0,0);
-
- set_ddbox(dd,bMasterState,cr,ir,state_global->box,
- TRUE,cgs_gl,state_global->x,&ddbox);
-
- get_cg_distribution(fplog,step,dd,cgs_gl,
- state_global->box,&ddbox,state_global->x);
-
- dd_distribute_state(dd,cgs_gl,
- state_global,state_local,f);
-
- dd_make_local_cgs(dd,&top_local->cgs);
-
- if (dd->ncg_home > fr->cg_nalloc)
- {
- dd_realloc_fr_cg(fr,dd->ncg_home);
- }
- calc_cgcm(fplog,0,dd->ncg_home,
- &top_local->cgs,state_local->x,fr->cg_cm);
-
- inc_nrnb(nrnb,eNR_CGCM,dd->nat_home);
-
- dd_set_cginfo(dd->index_gl,0,dd->ncg_home,fr,comm->bLocalCG);
-
- cg0 = 0;
- }
- else if (state_local->ddp_count != dd->ddp_count)
- {
- if (state_local->ddp_count > dd->ddp_count)
- {
- gmx_fatal(FARGS,"Internal inconsistency state_local->ddp_count (%d) > dd->ddp_count (%d)",state_local->ddp_count,dd->ddp_count);
- }
-
- if (state_local->ddp_count_cg_gl != state_local->ddp_count)
- {
- gmx_fatal(FARGS,"Internal inconsistency state_local->ddp_count_cg_gl (%d) != state_local->ddp_count (%d)",state_local->ddp_count_cg_gl,state_local->ddp_count);
- }
-
- /* Clear the old state */
- clear_dd_indices(dd,0,0);
-
- /* Build the new indices */
- rebuild_cgindex(dd,cgs_gl->index,state_local);
- make_dd_indices(dd,cgs_gl->index,0);
-
- /* Redetermine the cg COMs */
- calc_cgcm(fplog,0,dd->ncg_home,
- &top_local->cgs,state_local->x,fr->cg_cm);
-
- inc_nrnb(nrnb,eNR_CGCM,dd->nat_home);
-
- dd_set_cginfo(dd->index_gl,0,dd->ncg_home,fr,comm->bLocalCG);
-
- set_ddbox(dd,bMasterState,cr,ir,state_local->box,
- TRUE,&top_local->cgs,state_local->x,&ddbox);
-
- bRedist = comm->bDynLoadBal;
- }
- else
- {
- /* We have the full state, only redistribute the cgs */
-
- /* Clear the non-home indices */
- clear_dd_indices(dd,dd->ncg_home,dd->nat_home);
-
- /* Avoid global communication for dim's without pbc and -gcom */
- if (!bNStGlobalComm)
- {
- copy_rvec(comm->box0 ,ddbox.box0 );
- copy_rvec(comm->box_size,ddbox.box_size);
- }
- set_ddbox(dd,bMasterState,cr,ir,state_local->box,
- bNStGlobalComm,&top_local->cgs,state_local->x,&ddbox);
-
- bBoxChanged = TRUE;
- bRedist = TRUE;
- }
- /* For dim's without pbc and -gcom */
- copy_rvec(ddbox.box0 ,comm->box0 );
- copy_rvec(ddbox.box_size,comm->box_size);
-
- set_dd_cell_sizes(dd,&ddbox,dynamic_dd_box(&ddbox,ir),bMasterState,bDoDLB,
- step,wcycle);
-
- if (comm->nstDDDumpGrid > 0 && step % comm->nstDDDumpGrid == 0)
- {
- write_dd_grid_pdb("dd_grid",step,dd,state_local->box,&ddbox);
- }
-
- /* Check if we should sort the charge groups */
- if (comm->nstSortCG > 0)
- {
- bSortCG = (bMasterState ||
- (bRedist && (step % comm->nstSortCG == 0)));
- }
- else
- {
- bSortCG = FALSE;
- }
-
- ncg_home_old = dd->ncg_home;
-
- if (bRedist)
- {
- cg0 = dd_redistribute_cg(fplog,step,dd,ddbox.tric_dir,
- state_local,f,fr,mdatoms,
- !bSortCG,nrnb);
- }
-
- get_nsgrid_boundaries(fr->ns.grid,dd,
- state_local->box,&ddbox,&comm->cell_x0,&comm->cell_x1,
- dd->ncg_home,fr->cg_cm,
- cell_ns_x0,cell_ns_x1,&grid_density);
-
- if (bBoxChanged)
- {
- comm_dd_ns_cell_sizes(dd,&ddbox,cell_ns_x0,cell_ns_x1,step);
- }
-
- copy_ivec(fr->ns.grid->n,ncells_old);
- grid_first(fplog,fr->ns.grid,dd,&ddbox,fr->ePBC,
- state_local->box,cell_ns_x0,cell_ns_x1,
- fr->rlistlong,grid_density);
- /* We need to store tric_dir for dd_get_ns_ranges called from ns.c */
- copy_ivec(ddbox.tric_dir,comm->tric_dir);
-
- if (bSortCG)
- {
- /* Sort the state on charge group position.
- * This enables exact restarts from this step.
- * It also improves performance by about 15% with larger numbers
- * of atoms per node.
- */
-
- /* Fill the ns grid with the home cell,
- * so we can sort with the indices.
- */
- set_zones_ncg_home(dd);
- fill_grid(fplog,&comm->zones,fr->ns.grid,dd->ncg_home,
- 0,dd->ncg_home,fr->cg_cm);
-
- /* Check if we can user the old order and ns grid cell indices
- * of the charge groups to sort the charge groups efficiently.
- */
- bResortAll = (bMasterState ||
- fr->ns.grid->n[XX] != ncells_old[XX] ||
- fr->ns.grid->n[YY] != ncells_old[YY] ||
- fr->ns.grid->n[ZZ] != ncells_old[ZZ]);
-
- if (debug)
- {
- fprintf(debug,"Step %s, sorting the %d home charge groups\n",
- gmx_step_str(step,sbuf),dd->ncg_home);
- }
- dd_sort_state(dd,ir->ePBC,fr->cg_cm,fr,state_local,
- bResortAll ? -1 : ncg_home_old);
- /* Rebuild all the indices */
- cg0 = 0;
- ga2la_clear(dd->ga2la);
- }
-
- /* Setup up the communication and communicate the coordinates */
- setup_dd_communication(dd,state_local->box,&ddbox,fr);
-
- /* Set the indices */
- make_dd_indices(dd,cgs_gl->index,cg0);
-
- /* Set the charge group boundaries for neighbor searching */
- set_cg_boundaries(&comm->zones);
-
- /*
- write_dd_pdb("dd_home",step,"dump",top_global,cr,
- -1,state_local->x,state_local->box);
- */
-
- /* Extract a local topology from the global topology */
- for(i=0; i<dd->ndim; i++)
- {
- np[dd->dim[i]] = comm->cd[i].np;
- }
- dd_make_local_top(fplog,dd,&comm->zones,dd->npbcdim,state_local->box,
- comm->cellsize_min,np,
- fr,vsite,top_global,top_local);
-
- /* Set up the special atom communication */
- n = comm->nat[ddnatZONE];
- for(i=ddnatZONE+1; i<ddnatNR; i++)
- {
- switch(i)
- {
- case ddnatVSITE:
- if (vsite && vsite->n_intercg_vsite)
- {
- n = dd_make_local_vsites(dd,n,top_local->idef.il);
- }
- break;
- case ddnatCON:
- if (dd->bInterCGcons)
- {
- /* Only for inter-cg constraints we need special code */
- n = dd_make_local_constraints(dd,n,top_global,
- constr,ir->nProjOrder,
- &top_local->idef.il[F_CONSTR]);
- }
- break;
- default:
- gmx_incons("Unknown special atom type setup");
- }
- comm->nat[i] = n;
- }
-
- /* Make space for the extra coordinates for virtual site
- * or constraint communication.
- */
- state_local->natoms = comm->nat[ddnatNR-1];
- if (state_local->natoms > state_local->nalloc)
- {
- dd_realloc_state(state_local,f,state_local->natoms);
- }
-
- if (fr->bF_NoVirSum)
- {
- if (vsite && vsite->n_intercg_vsite)
- {
- nat_f_novirsum = comm->nat[ddnatVSITE];
- }
- else
- {
- if (EEL_FULL(ir->coulombtype) && dd->n_intercg_excl > 0)
- {
- nat_f_novirsum = dd->nat_tot;
- }
- else
- {
- nat_f_novirsum = dd->nat_home;
- }
- }
- }
- else
- {
- nat_f_novirsum = 0;
- }
-
- /* Set the number of atoms required for the force calculation.
- * Forces need to be constrained when using a twin-range setup
- * or with energy minimization. For simple simulations we could
- * avoid some allocation, zeroing and copying, but this is
- * probably not worth the complications ande checking.
- */
- forcerec_set_ranges(fr,dd->ncg_home,dd->ncg_tot,
- dd->nat_tot,comm->nat[ddnatCON],nat_f_novirsum);
-
- /* We make the all mdatoms up to nat_tot_con.
- * We could save some work by only setting invmass
- * between nat_tot and nat_tot_con.
- */
- /* This call also sets the new number of home particles to dd->nat_home */
- atoms2md(top_global,ir,
- comm->nat[ddnatCON],dd->gatindex,0,dd->nat_home,mdatoms);
-
- /* Now we have the charges we can sort the FE interactions */
- dd_sort_local_top(dd,mdatoms,top_local);
-
- if (shellfc)
- {
- /* Make the local shell stuff, currently no communication is done */
- make_local_shells(cr,mdatoms,shellfc);
- }
-
- if (ir->implicit_solvent)
- {
- make_local_gb(cr,fr->born,ir->gb_algorithm);
- }
-
- if (!(cr->duty & DUTY_PME))
- {
- /* Send the charges to our PME only node */
- gmx_pme_send_q(cr,mdatoms->nChargePerturbed,
- mdatoms->chargeA,mdatoms->chargeB,
- dd_pme_maxshift_x(dd),dd_pme_maxshift_y(dd));
- }
-
- if (constr)
- {
- set_constraints(constr,top_local,ir,mdatoms,cr);
- }
-
- if (ir->ePull != epullNO)
- {
- /* Update the local pull groups */
- dd_make_local_pull_groups(dd,ir->pull,mdatoms);
- }
-
- add_dd_statistics(dd);
-
- /* Make sure we only count the cycles for this DD partitioning */
- clear_dd_cycle_counts(dd);
-
- /* Because the order of the atoms might have changed since
- * the last vsite construction, we need to communicate the constructing
- * atom coordinates again (for spreading the forces this MD step).
- */
- dd_move_x_vsites(dd,state_local->box,state_local->x);
-
- if (comm->nstDDDump > 0 && step % comm->nstDDDump == 0)
- {
- dd_move_x(dd,state_local->box,state_local->x);
- write_dd_pdb("dd_dump",step,"dump",top_global,cr,
- -1,state_local->x,state_local->box);
- }
-
- /* Store the partitioning step */
- comm->partition_step = step;
-
- /* Increase the DD partitioning counter */
- dd->ddp_count++;
- /* The state currently matches this DD partitioning count, store it */
- state_local->ddp_count = dd->ddp_count;
- if (bMasterState)
- {
- /* The DD master node knows the complete cg distribution,
- * store the count so we can possibly skip the cg info communication.
- */
- comm->master_cg_ddp_count = (bSortCG ? 0 : dd->ddp_count);
- }
-
- if (comm->DD_debug > 0)
- {
- /* Set the env var GMX_DD_DEBUG if you suspect corrupted indices */
- check_index_consistency(dd,top_global->natoms,ncg_mtop(top_global),
- "after partitioning");
- }
-}
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2008, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include "gmx_wallcycle.h"
-#include "gmx_cyclecounter.h"
-#include "smalloc.h"
-#include "gmx_fatal.h"
-
-#ifdef GMX_LIB_MPI
-#include <mpi.h>
-#endif
-#ifdef GMX_THREADS
-#include "tmpi.h"
-#endif
-
-typedef struct
-{
- int n;
- gmx_cycles_t c;
- gmx_cycles_t start;
- gmx_cycles_t last;
-} wallcc_t;
-
-typedef struct gmx_wallcycle
-{
- wallcc_t *wcc;
- /* variables for testing/debugging */
- gmx_bool wc_barrier;
- wallcc_t *wcc_all;
- int wc_depth;
- int ewc_prev;
- gmx_cycles_t cycle_prev;
- gmx_large_int_t reset_counters;
-#ifdef GMX_MPI
- MPI_Comm mpi_comm_mygroup;
-#endif
-} gmx_wallcycle_t_t;
-
-/* Each name should not exceed 19 characters */
-static const char *wcn[ewcNR] =
-{ "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load", "DD comm. bounds", "Vsite constr.", "Send X to PME", "Comm. coord.", "Neighbor search", "Born radii", "Force", "Wait + Comm. F", "PME mesh", "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME solve", "Wait + Comm. X/F", "Wait + Recv. PME F", "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies", "Test" };
-
-gmx_bool wallcycle_have_counter(void)
-{
- return gmx_cycles_have_counter();
-}
-
-gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr)
-{
- gmx_wallcycle_t wc;
-
-
- if (!wallcycle_have_counter())
- {
- return NULL;
- }
-
- snew(wc,1);
-
- wc->wc_barrier = FALSE;
- wc->wcc_all = NULL;
- wc->wc_depth = 0;
- wc->ewc_prev = -1;
- wc->reset_counters = resetstep;
-
-#ifdef GMX_MPI
- if (PAR(cr) && getenv("GMX_CYCLE_BARRIER") != NULL)
- {
- if (fplog)
- {
- fprintf(fplog,"\nWill call MPI_Barrier before each cycle start/stop call\n\n");
- }
- wc->wc_barrier = TRUE;
- wc->mpi_comm_mygroup = cr->mpi_comm_mygroup;
- }
-#endif
-
- snew(wc->wcc,ewcNR);
- if (getenv("GMX_CYCLE_ALL") != NULL)
- {
-/*#ifndef GMX_THREADS*/
- if (fplog)
- {
- fprintf(fplog,"\nWill time all the code during the run\n\n");
- }
- snew(wc->wcc_all,ewcNR*ewcNR);
-/*#else*/
- gmx_fatal(FARGS, "GMX_CYCLE_ALL is incompatible with threaded code");
-/*#endif*/
- }
-
- return wc;
-}
-
-void wallcycle_destroy(gmx_wallcycle_t wc)
-{
- if (wc == NULL)
- {
- return;
- }
-
- if (wc->wcc != NULL)
- {
- sfree(wc->wcc);
- }
- if (wc->wcc_all != NULL)
- {
- sfree(wc->wcc_all);
- }
- sfree(wc);
-}
-
-static void wallcycle_all_start(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
-{
- wc->ewc_prev = ewc;
- wc->cycle_prev = cycle;
-}
-
-static void wallcycle_all_stop(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
-{
- wc->wcc_all[wc->ewc_prev*ewcNR+ewc].n += 1;
- wc->wcc_all[wc->ewc_prev*ewcNR+ewc].c += cycle - wc->cycle_prev;
-}
-
-void wallcycle_start(gmx_wallcycle_t wc, int ewc)
-{
- gmx_cycles_t cycle;
-
- if (wc == NULL)
- {
- return;
- }
-
-#ifdef GMX_MPI
- if (wc->wc_barrier)
- {
- MPI_Barrier(wc->mpi_comm_mygroup);
- }
-#endif
-
- cycle = gmx_cycles_read();
- wc->wcc[ewc].start = cycle;
- if (wc->wcc_all != NULL)
- {
- wc->wc_depth++;
- if (ewc == ewcRUN)
- {
- wallcycle_all_start(wc,ewc,cycle);
- }
- else if (wc->wc_depth == 3)
- {
- wallcycle_all_stop(wc,ewc,cycle);
- }
- }
-}
-
-double wallcycle_stop(gmx_wallcycle_t wc, int ewc)
-{
- gmx_cycles_t cycle,last;
-
- if (wc == NULL)
- {
- return 0;
- }
-
-#ifdef GMX_MPI
- if (wc->wc_barrier)
- {
- MPI_Barrier(wc->mpi_comm_mygroup);
- }
-#endif
-
- cycle = gmx_cycles_read();
- last = cycle - wc->wcc[ewc].start;
- wc->wcc[ewc].c += last;
- wc->wcc[ewc].n++;
- if (wc->wcc_all)
- {
- wc->wc_depth--;
- if (ewc == ewcRUN)
- {
- wallcycle_all_stop(wc,ewc,cycle);
- }
- else if (wc->wc_depth == 2)
- {
- wallcycle_all_start(wc,ewc,cycle);
- }
- }
-
- return last;
-}
-
-void wallcycle_reset_all(gmx_wallcycle_t wc)
-{
- int i;
-
- if (wc == NULL)
- {
- return;
- }
-
- for(i=0; i<ewcNR; i++)
- {
- wc->wcc[i].n = 0;
- wc->wcc[i].c = 0;
- wc->wcc[i].start = 0;
- wc->wcc[i].last = 0;
- }
-}
-
-void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc,double cycles[])
-{
- wallcc_t *wcc;
- double cycles_n[ewcNR],buf[ewcNR],*cyc_all,*buf_all;
- int i;
-
- if (wc == NULL)
- {
- return;
- }
-
- wcc = wc->wcc;
-
- if (wcc[ewcDDCOMMLOAD].n > 0)
- {
- wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMLOAD].c;
- }
- if (wcc[ewcDDCOMMBOUND].n > 0)
- {
- wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMBOUND].c;
- }
- if (cr->npmenodes == 0)
- {
- /* All nodes do PME (or no PME at all) */
- if (wcc[ewcPMEMESH].n > 0)
- {
- wcc[ewcFORCE].c -= wcc[ewcPMEMESH].c;
- }
- }
- else
- {
- /* The are PME-only nodes */
- if (wcc[ewcPMEMESH].n > 0)
- {
- /* This must be a PME only node, calculate the Wait + Comm. time */
- wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
- }
- }
-
- /* Store the cycles in a double buffer for summing */
- for(i=0; i<ewcNR; i++)
- {
- cycles_n[i] = (double)wcc[i].n;
- cycles[i] = (double)wcc[i].c;
- }
-
-#ifdef GMX_MPI
- if (cr->nnodes > 1)
- {
- MPI_Allreduce(cycles_n,buf,ewcNR,MPI_DOUBLE,MPI_MAX,
- cr->mpi_comm_mysim);
- for(i=0; i<ewcNR; i++)
- {
- wcc[i].n = (int)(buf[i] + 0.5);
- }
- MPI_Allreduce(cycles,buf,ewcNR,MPI_DOUBLE,MPI_SUM,
- cr->mpi_comm_mysim);
- for(i=0; i<ewcNR; i++)
- {
- cycles[i] = buf[i];
- }
-
- if (wc->wcc_all != NULL)
- {
- snew(cyc_all,ewcNR*ewcNR);
- snew(buf_all,ewcNR*ewcNR);
- for(i=0; i<ewcNR*ewcNR; i++)
- {
- cyc_all[i] = wc->wcc_all[i].c;
- }
- MPI_Allreduce(cyc_all,buf_all,ewcNR*ewcNR,MPI_DOUBLE,MPI_SUM,
- cr->mpi_comm_mysim);
- for(i=0; i<ewcNR*ewcNR; i++)
- {
- wc->wcc_all[i].c = buf_all[i];
- }
- sfree(buf_all);
- sfree(cyc_all);
- }
- }
-#endif
-}
-
-static void print_cycles(FILE *fplog, double c2t, const char *name, int nnodes,
- int n, double c, double tot)
-{
- char num[11];
-
- if (c > 0)
- {
- if (n > 0)
- {
- sprintf(num,"%10d",n);
- }
- else
- {
- sprintf(num," ");
- }
- fprintf(fplog," %-19s %4d %10s %12.3f %10.1f %5.1f\n",
- name,nnodes,num,c*1e-9,c*c2t,100*c/tot);
- }
-}
-
-static gmx_bool subdivision(int ewc)
-{
- return (ewc >= ewcPME_REDISTXF && ewc <= ewcPME_SOLVE);
-}
-
-void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
- gmx_wallcycle_t wc, double cycles[])
-{
- double c2t,tot,sum;
- int i,j,npp;
- char buf[STRLEN];
- const char *myline = "-----------------------------------------------------------------------";
-
- if (wc == NULL)
- {
- return;
- }
-
- if (npme > 0)
- {
- npp = nnodes - npme;
- }
- else
- {
- npp = nnodes;
- npme = nnodes;
- }
- tot = cycles[ewcRUN];
- /* Conversion factor from cycles to seconds */
- if (tot > 0)
- {
- c2t = nnodes*realtime/tot;
- }
- else
- {
- c2t = 0;
- }
-
- fprintf(fplog,"\n R E A L C Y C L E A N D T I M E A C C O U N T I N G\n\n");
-
- fprintf(fplog," Computing: Nodes Number G-Cycles Seconds %c\n",'%');
- fprintf(fplog,"%s\n",myline);
- sum = 0;
- for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
- {
- if (!subdivision(i))
- {
- print_cycles(fplog,c2t,wcn[i],
- (i==ewcPMEMESH || i==ewcPMEWAITCOMM) ? npme : npp,
- wc->wcc[i].n,cycles[i],tot);
- sum += cycles[i];
- }
- }
- if (wc->wcc_all != NULL)
- {
- for(i=0; i<ewcNR; i++)
- {
- for(j=0; j<ewcNR; j++)
- {
- sprintf(buf,"%-9s",wcn[i]);
- buf[9] = ' ';
- sprintf(buf+10,"%-9s",wcn[j]);
- buf[19] = '\0';
- print_cycles(fplog,c2t,buf,
- (i==ewcPMEMESH || i==ewcPMEWAITCOMM) ? npme : npp,
- wc->wcc_all[i*ewcNR+j].n,
- wc->wcc_all[i*ewcNR+j].c,
- tot);
- }
- }
- }
- print_cycles(fplog,c2t,"Rest",npp,0,tot-sum,tot);
- fprintf(fplog,"%s\n",myline);
- print_cycles(fplog,c2t,"Total",nnodes,0,tot,tot);
- fprintf(fplog,"%s\n",myline);
-
- if (wc->wcc[ewcPMEMESH].n > 0)
- {
- fprintf(fplog,"%s\n",myline);
- for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
- {
- if (subdivision(i))
- {
- print_cycles(fplog,c2t,wcn[i],
- (i>=ewcPMEMESH || i<=ewcPME_SOLVE) ? npme : npp,
- wc->wcc[i].n,cycles[i],tot);
- }
- }
- fprintf(fplog,"%s\n",myline);
- }
-
- if (cycles[ewcMoveE] > tot*0.05)
- {
- sprintf(buf,
- "NOTE: %d %% of the run time was spent communicating energies,\n"
- " you might want to use the -gcom option of mdrun\n",
- (int)(100*cycles[ewcMoveE]/tot+0.5));
- if (fplog)
- {
- fprintf(fplog,"\n%s\n",buf);
- }
- /* Only the sim master calls this function, so always print to stderr */
- fprintf(stderr,"\n%s\n",buf);
- }
-}
-
-extern gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
-{
- if (wc == NULL)
- {
- return -1;
- }
-
- return wc->reset_counters;
-}
-
-extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters)
-{
- if (wc == NULL)
- return;
-
- wc->reset_counters = reset_counters;
-}
+++ /dev/null
-libdir=@LIB_INSTALL_DIR@
-includedir=@INCL_INSTALL_DIR@
-
-Name: libmd
-Description: Gromacs md lib
-URL: http://www.gromacs.org
-Version: @PROJECT_VERSION@
-Requires: libgmx@LIBSUFFIX@ @PKG_FFT@ @PKG_XML@
-Libs.private: -lm @CMAKE_THREAD_LIBS_INIT@
-Libs: -L${libdir} -lmd@LIBSUFFIX@ @PKG_FFT_LIBS@
-Cflags: -I${includedir} @PKG_CFLAGS@
-
+++ /dev/null
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libmd
-Description: Gromacs md lib
-URL: http://www.gromacs.org
-Version: @VERSION@
-Requires: libgmx@LIBSUFFIX@ @PKG_FFT@ @PKG_XML@
-Libs: -L${libdir} -lmd@LIBSUFFIX@ @PKG_FFT_LIBS@ @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ -lm
-Cflags: -I${includedir} @PTHREAD_CFLAGS@ @PKG_CFLAGS@
-
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROwing Monsters And Cloning Shrimps
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <float.h>
-#include "typedefs.h"
-#include "string2.h"
-#include "mdebin.h"
-#include "smalloc.h"
-#include "physics.h"
-#include "enxio.h"
-#include "vec.h"
-#include "disre.h"
-#include "main.h"
-#include "network.h"
-#include "names.h"
-#include "orires.h"
-#include "constr.h"
-#include "mtop_util.h"
-#include "xvgr.h"
-#include "gmxfio.h"
-
-#include "mdebin_bar.h"
-
-
-static const char *conrmsd_nm[] = { "Constr. rmsd", "Constr.2 rmsd" };
-
-static const char *boxs_nm[] = { "Box-X", "Box-Y", "Box-Z" };
-
-static const char *tricl_boxs_nm[] = {
- "Box-XX", "Box-YY", "Box-ZZ",
- "Box-YX", "Box-ZX", "Box-ZY"
-};
-
-static const char *vol_nm[] = { "Volume" };
-
-static const char *dens_nm[] = {"Density" };
-
-static const char *pv_nm[] = {"pV" };
-
-static const char *enthalpy_nm[] = {"Enthalpy" };
-
-static const char *boxvel_nm[] = {
- "Box-Vel-XX", "Box-Vel-YY", "Box-Vel-ZZ",
- "Box-Vel-YX", "Box-Vel-ZX", "Box-Vel-ZY"
-};
-
-#define NBOXS asize(boxs_nm)
-#define NTRICLBOXS asize(tricl_boxs_nm)
-
-static gmx_bool bTricl,bDynBox;
-static int f_nre=0,epc,etc,nCrmsd;
-
-
-
-
-
-t_mdebin *init_mdebin(ener_file_t fp_ene,
- const gmx_mtop_t *mtop,
- const t_inputrec *ir,
- FILE *fp_dhdl)
-{
- const char *ener_nm[F_NRE];
- static const char *vir_nm[] = {
- "Vir-XX", "Vir-XY", "Vir-XZ",
- "Vir-YX", "Vir-YY", "Vir-YZ",
- "Vir-ZX", "Vir-ZY", "Vir-ZZ"
- };
- static const char *sv_nm[] = {
- "ShakeVir-XX", "ShakeVir-XY", "ShakeVir-XZ",
- "ShakeVir-YX", "ShakeVir-YY", "ShakeVir-YZ",
- "ShakeVir-ZX", "ShakeVir-ZY", "ShakeVir-ZZ"
- };
- static const char *fv_nm[] = {
- "ForceVir-XX", "ForceVir-XY", "ForceVir-XZ",
- "ForceVir-YX", "ForceVir-YY", "ForceVir-YZ",
- "ForceVir-ZX", "ForceVir-ZY", "ForceVir-ZZ"
- };
- static const char *pres_nm[] = {
- "Pres-XX","Pres-XY","Pres-XZ",
- "Pres-YX","Pres-YY","Pres-YZ",
- "Pres-ZX","Pres-ZY","Pres-ZZ"
- };
- static const char *surft_nm[] = {
- "#Surf*SurfTen"
- };
- static const char *mu_nm[] = {
- "Mu-X", "Mu-Y", "Mu-Z"
- };
- static const char *vcos_nm[] = {
- "2CosZ*Vel-X"
- };
- static const char *visc_nm[] = {
- "1/Viscosity"
- };
- static const char *baro_nm[] = {
- "Barostat"
- };
-
- char **grpnms;
- const gmx_groups_t *groups;
- char **gnm;
- char buf[256];
- const char *bufi;
- t_mdebin *md;
- int i,j,ni,nj,n,nh,k,kk,ncon,nset;
- gmx_bool bBHAM,bNoseHoover,b14;
-
- snew(md,1);
-
- if (EI_DYNAMICS(ir->eI))
- {
- md->delta_t = ir->delta_t;
- }
- else
- {
- md->delta_t = 0;
- }
-
- groups = &mtop->groups;
-
- bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
- b14 = (gmx_mtop_ftype_count(mtop,F_LJ14) > 0 ||
- gmx_mtop_ftype_count(mtop,F_LJC14_Q) > 0);
-
- ncon = gmx_mtop_ftype_count(mtop,F_CONSTR);
- nset = gmx_mtop_ftype_count(mtop,F_SETTLE);
- md->bConstr = (ncon > 0 || nset > 0);
- md->bConstrVir = FALSE;
- if (md->bConstr) {
- if (ncon > 0 && ir->eConstrAlg == econtLINCS) {
- if (ir->eI == eiSD2)
- md->nCrmsd = 2;
- else
- md->nCrmsd = 1;
- }
- md->bConstrVir = (getenv("GMX_CONSTRAINTVIR") != NULL);
- } else {
- md->nCrmsd = 0;
- }
-
- /* Energy monitoring */
- for(i=0;i<egNR;i++)
- {
- md->bEInd[i]=FALSE;
- }
-
-#ifndef GMX_OPENMM
- for(i=0; i<F_NRE; i++)
- {
- md->bEner[i] = FALSE;
- if (i == F_LJ)
- md->bEner[i] = !bBHAM;
- else if (i == F_BHAM)
- md->bEner[i] = bBHAM;
- else if (i == F_EQM)
- md->bEner[i] = ir->bQMMM;
- else if (i == F_COUL_LR)
- md->bEner[i] = (ir->rcoulomb > ir->rlist);
- else if (i == F_LJ_LR)
- md->bEner[i] = (!bBHAM && ir->rvdw > ir->rlist);
- else if (i == F_BHAM_LR)
- md->bEner[i] = (bBHAM && ir->rvdw > ir->rlist);
- else if (i == F_RF_EXCL)
- md->bEner[i] = (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC);
- else if (i == F_COUL_RECIP)
- md->bEner[i] = EEL_FULL(ir->coulombtype);
- else if (i == F_LJ14)
- md->bEner[i] = b14;
- else if (i == F_COUL14)
- md->bEner[i] = b14;
- else if (i == F_LJC14_Q || i == F_LJC_PAIRS_NB)
- md->bEner[i] = FALSE;
- else if ((i == F_DVDL) || (i == F_DKDL))
- md->bEner[i] = (ir->efep != efepNO);
- else if (i == F_DHDL_CON)
- md->bEner[i] = (ir->efep != efepNO && md->bConstr);
- else if ((interaction_function[i].flags & IF_VSITE) ||
- (i == F_CONSTR) || (i == F_CONSTRNC) || (i == F_SETTLE))
- md->bEner[i] = FALSE;
- else if ((i == F_COUL_SR) || (i == F_EPOT) || (i == F_PRES) || (i==F_EQM))
- md->bEner[i] = TRUE;
- else if ((i == F_GBPOL) && ir->implicit_solvent==eisGBSA)
- md->bEner[i] = TRUE;
- else if ((i == F_NPSOLVATION) && ir->implicit_solvent==eisGBSA && (ir->sa_algorithm != esaNO))
- md->bEner[i] = TRUE;
- else if ((i == F_GB12) || (i == F_GB13) || (i == F_GB14))
- md->bEner[i] = FALSE;
- else if ((i == F_ETOT) || (i == F_EKIN) || (i == F_TEMP))
- md->bEner[i] = EI_DYNAMICS(ir->eI);
- else if (i==F_VTEMP)
- md->bEner[i] = (EI_DYNAMICS(ir->eI) && getenv("GMX_VIRIAL_TEMPERATURE"));
- else if (i == F_DISPCORR || i == F_PDISPCORR)
- md->bEner[i] = (ir->eDispCorr != edispcNO);
- else if (i == F_DISRESVIOL)
- md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_DISRES) > 0);
- else if (i == F_ORIRESDEV)
- md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0);
- else if (i == F_CONNBONDS)
- md->bEner[i] = FALSE;
- else if (i == F_COM_PULL)
- md->bEner[i] = (ir->ePull == epullUMBRELLA || ir->ePull == epullCONST_F);
- else if (i == F_ECONSERVED)
- md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
- (ir->epc == epcNO || ir->epc==epcMTTK));
- else
- md->bEner[i] = (gmx_mtop_ftype_count(mtop,i) > 0);
- }
-#else
- /* OpenMM always produces only the following 4 energy terms */
- md->bEner[F_EPOT] = TRUE;
- md->bEner[F_EKIN] = TRUE;
- md->bEner[F_ETOT] = TRUE;
- md->bEner[F_TEMP] = TRUE;
-#endif
-
- md->f_nre=0;
- for(i=0; i<F_NRE; i++)
- {
- if (md->bEner[i])
- {
- /* FIXME: The constness should not be cast away */
- /*ener_nm[f_nre]=(char *)interaction_function[i].longname;*/
- ener_nm[md->f_nre]=interaction_function[i].longname;
- md->f_nre++;
- }
- }
-
- md->epc = ir->epc;
- for (i=0;i<DIM;i++)
- {
- for (j=0;j<DIM;j++)
- {
- md->ref_p[i][j] = ir->ref_p[i][j];
- }
- }
- md->bTricl = TRICLINIC(ir->compress) || TRICLINIC(ir->deform);
- md->bDynBox = DYNAMIC_BOX(*ir);
- md->etc = ir->etc;
- md->bNHC_trotter = IR_NVT_TROTTER(ir);
- md->bMTTK = IR_NPT_TROTTER(ir);
-
- md->ebin = mk_ebin();
- /* Pass NULL for unit to let get_ebin_space determine the units
- * for interaction_function[i].longname
- */
- md->ie = get_ebin_space(md->ebin,md->f_nre,ener_nm,NULL);
- if (md->nCrmsd)
- {
- /* This should be called directly after the call for md->ie,
- * such that md->iconrmsd follows directly in the list.
- */
- md->iconrmsd = get_ebin_space(md->ebin,md->nCrmsd,conrmsd_nm,"");
- }
- if (md->bDynBox)
- {
- md->ib = get_ebin_space(md->ebin,
- md->bTricl ? NTRICLBOXS : NBOXS,
- md->bTricl ? tricl_boxs_nm : boxs_nm,
- unit_length);
- md->ivol = get_ebin_space(md->ebin, 1, vol_nm, unit_volume);
- md->idens = get_ebin_space(md->ebin, 1, dens_nm, unit_density_SI);
- md->ipv = get_ebin_space(md->ebin, 1, pv_nm, unit_energy);
- md->ienthalpy = get_ebin_space(md->ebin, 1, enthalpy_nm, unit_energy);
- }
- if (md->bConstrVir)
- {
- md->isvir = get_ebin_space(md->ebin,asize(sv_nm),sv_nm,unit_energy);
- md->ifvir = get_ebin_space(md->ebin,asize(fv_nm),fv_nm,unit_energy);
- }
- md->ivir = get_ebin_space(md->ebin,asize(vir_nm),vir_nm,unit_energy);
- md->ipres = get_ebin_space(md->ebin,asize(pres_nm),pres_nm,unit_pres_bar);
- md->isurft = get_ebin_space(md->ebin,asize(surft_nm),surft_nm,
- unit_surft_bar);
- if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
- {
- md->ipc = get_ebin_space(md->ebin,md->bTricl ? 6 : 3,
- boxvel_nm,unit_vel);
- }
- md->imu = get_ebin_space(md->ebin,asize(mu_nm),mu_nm,unit_dipole_D);
- if (ir->cos_accel != 0)
- {
- md->ivcos = get_ebin_space(md->ebin,asize(vcos_nm),vcos_nm,unit_vel);
- md->ivisc = get_ebin_space(md->ebin,asize(visc_nm),visc_nm,
- unit_invvisc_SI);
- }
-
- /* Energy monitoring */
- for(i=0;i<egNR;i++)
- {
- md->bEInd[i] = FALSE;
- }
- md->bEInd[egCOULSR] = TRUE;
- md->bEInd[egLJSR ] = TRUE;
-
- if (ir->rcoulomb > ir->rlist)
- {
- md->bEInd[egCOULLR] = TRUE;
- }
- if (!bBHAM)
- {
- if (ir->rvdw > ir->rlist)
- {
- md->bEInd[egLJLR] = TRUE;
- }
- }
- else
- {
- md->bEInd[egLJSR] = FALSE;
- md->bEInd[egBHAMSR] = TRUE;
- if (ir->rvdw > ir->rlist)
- {
- md->bEInd[egBHAMLR] = TRUE;
- }
- }
- if (b14)
- {
- md->bEInd[egLJ14] = TRUE;
- md->bEInd[egCOUL14] = TRUE;
- }
- md->nEc=0;
- for(i=0; (i<egNR); i++)
- {
- if (md->bEInd[i])
- {
- md->nEc++;
- }
- }
-
- n=groups->grps[egcENER].nr;
- md->nEg=n;
- md->nE=(n*(n+1))/2;
- snew(md->igrp,md->nE);
- if (md->nE > 1)
- {
- n=0;
- snew(gnm,md->nEc);
- for(k=0; (k<md->nEc); k++)
- {
- snew(gnm[k],STRLEN);
- }
- for(i=0; (i<groups->grps[egcENER].nr); i++)
- {
- ni=groups->grps[egcENER].nm_ind[i];
- for(j=i; (j<groups->grps[egcENER].nr); j++)
- {
- nj=groups->grps[egcENER].nm_ind[j];
- for(k=kk=0; (k<egNR); k++)
- {
- if (md->bEInd[k])
- {
- sprintf(gnm[kk],"%s:%s-%s",egrp_nm[k],
- *(groups->grpname[ni]),*(groups->grpname[nj]));
- kk++;
- }
- }
- md->igrp[n]=get_ebin_space(md->ebin,md->nEc,
- (const char **)gnm,unit_energy);
- n++;
- }
- }
- for(k=0; (k<md->nEc); k++)
- {
- sfree(gnm[k]);
- }
- sfree(gnm);
-
- if (n != md->nE)
- {
- gmx_incons("Number of energy terms wrong");
- }
- }
-
- md->nTC=groups->grps[egcTC].nr;
- md->nNHC = ir->opts.nhchainlength; /* shorthand for number of NH chains */
- if (md->bMTTK)
- {
- md->nTCP = 1; /* assume only one possible coupling system for barostat
- for now */
- }
- else
- {
- md->nTCP = 0;
- }
-
- if (md->etc == etcNOSEHOOVER) {
- if (md->bNHC_trotter) {
- md->mde_n = 2*md->nNHC*md->nTC;
- }
- else
- {
- md->mde_n = 2*md->nTC;
- }
- if (md->epc == epcMTTK)
- {
- md->mdeb_n = 2*md->nNHC*md->nTCP;
- }
- } else {
- md->mde_n = md->nTC;
- md->mdeb_n = 0;
- }
-
- snew(md->tmp_r,md->mde_n);
- snew(md->tmp_v,md->mde_n);
- snew(md->grpnms,md->mde_n);
- grpnms = md->grpnms;
-
- for(i=0; (i<md->nTC); i++)
- {
- ni=groups->grps[egcTC].nm_ind[i];
- sprintf(buf,"T-%s",*(groups->grpname[ni]));
- grpnms[i]=strdup(buf);
- }
- md->itemp=get_ebin_space(md->ebin,md->nTC,(const char **)grpnms,
- unit_temp_K);
-
- bNoseHoover = (getenv("GMX_NOSEHOOVER_CHAINS") != NULL); /* whether to print Nose-Hoover chains */
-
- if (md->etc == etcNOSEHOOVER)
- {
- if (bNoseHoover)
- {
- if (md->bNHC_trotter)
- {
- for(i=0; (i<md->nTC); i++)
- {
- ni=groups->grps[egcTC].nm_ind[i];
- bufi = *(groups->grpname[ni]);
- for(j=0; (j<md->nNHC); j++)
- {
- sprintf(buf,"Xi-%d-%s",j,bufi);
- grpnms[2*(i*md->nNHC+j)]=strdup(buf);
- sprintf(buf,"vXi-%d-%s",j,bufi);
- grpnms[2*(i*md->nNHC+j)+1]=strdup(buf);
- }
- }
- md->itc=get_ebin_space(md->ebin,md->mde_n,
- (const char **)grpnms,unit_invtime);
- if (md->bMTTK)
- {
- for(i=0; (i<md->nTCP); i++)
- {
- bufi = baro_nm[0]; /* All barostat DOF's together for now. */
- for(j=0; (j<md->nNHC); j++)
- {
- sprintf(buf,"Xi-%d-%s",j,bufi);
- grpnms[2*(i*md->nNHC+j)]=strdup(buf);
- sprintf(buf,"vXi-%d-%s",j,bufi);
- grpnms[2*(i*md->nNHC+j)+1]=strdup(buf);
- }
- }
- md->itcb=get_ebin_space(md->ebin,md->mdeb_n,
- (const char **)grpnms,unit_invtime);
- }
- }
- else
- {
- for(i=0; (i<md->nTC); i++)
- {
- ni=groups->grps[egcTC].nm_ind[i];
- bufi = *(groups->grpname[ni]);
- sprintf(buf,"Xi-%s",bufi);
- grpnms[2*i]=strdup(buf);
- sprintf(buf,"vXi-%s",bufi);
- grpnms[2*i+1]=strdup(buf);
- }
- md->itc=get_ebin_space(md->ebin,md->mde_n,
- (const char **)grpnms,unit_invtime);
- }
- }
- }
- else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
- md->etc == etcVRESCALE)
- {
- for(i=0; (i<md->nTC); i++)
- {
- ni=groups->grps[egcTC].nm_ind[i];
- sprintf(buf,"Lamb-%s",*(groups->grpname[ni]));
- grpnms[i]=strdup(buf);
- }
- md->itc=get_ebin_space(md->ebin,md->mde_n,(const char **)grpnms,"");
- }
-
- sfree(grpnms);
-
-
- md->nU=groups->grps[egcACC].nr;
- if (md->nU > 1)
- {
- snew(grpnms,3*md->nU);
- for(i=0; (i<md->nU); i++)
- {
- ni=groups->grps[egcACC].nm_ind[i];
- sprintf(buf,"Ux-%s",*(groups->grpname[ni]));
- grpnms[3*i+XX]=strdup(buf);
- sprintf(buf,"Uy-%s",*(groups->grpname[ni]));
- grpnms[3*i+YY]=strdup(buf);
- sprintf(buf,"Uz-%s",*(groups->grpname[ni]));
- grpnms[3*i+ZZ]=strdup(buf);
- }
- md->iu=get_ebin_space(md->ebin,3*md->nU,(const char **)grpnms,unit_vel);
- sfree(grpnms);
- }
-
- if ( fp_ene )
- {
- do_enxnms(fp_ene,&md->ebin->nener,&md->ebin->enm);
- }
-
- md->print_grpnms=NULL;
-
- /* check whether we're going to write dh histograms */
- md->dhc=NULL;
- if (ir->separate_dhdl_file == sepdhdlfileNO )
- {
- int i;
- snew(md->dhc, 1);
-
- mde_delta_h_coll_init(md->dhc, ir);
- md->fp_dhdl = NULL;
- }
- else
- {
- md->fp_dhdl = fp_dhdl;
- }
- md->dhdl_derivatives = (ir->dhdl_derivatives==dhdlderivativesYES);
- return md;
-}
-
-FILE *open_dhdl(const char *filename,const t_inputrec *ir,
- const output_env_t oenv)
-{
- FILE *fp;
- const char *dhdl="dH/d\\lambda",*deltag="\\DeltaH",*lambda="\\lambda";
- char title[STRLEN],label_x[STRLEN],label_y[STRLEN];
- char **setname;
- char buf[STRLEN];
-
- sprintf(label_x,"%s (%s)","Time",unit_time);
- if (ir->n_flambda == 0)
- {
- sprintf(title,"%s",dhdl);
- sprintf(label_y,"%s (%s %s)",
- dhdl,unit_energy,"[\\lambda]\\S-1\\N");
- }
- else
- {
- sprintf(title,"%s, %s",dhdl,deltag);
- sprintf(label_y,"(%s)",unit_energy);
- }
- fp = gmx_fio_fopen(filename,"w+");
- xvgr_header(fp,title,label_x,label_y,exvggtXNY,oenv);
-
- if (ir->delta_lambda == 0)
- {
- sprintf(buf,"T = %g (K), %s = %g",
- ir->opts.ref_t[0],lambda,ir->init_lambda);
- }
- else
- {
- sprintf(buf,"T = %g (K)",
- ir->opts.ref_t[0]);
- }
- xvgr_subtitle(fp,buf,oenv);
-
- if (ir->n_flambda > 0)
- {
- int nsets,s,nsi=0;
- /* g_bar has to determine the lambda values used in this simulation
- * from this xvg legend. */
- nsets = ( (ir->dhdl_derivatives==dhdlderivativesYES) ? 1 : 0) +
- ir->n_flambda;
- snew(setname,nsets);
- if (ir->dhdl_derivatives == dhdlderivativesYES)
- {
- sprintf(buf,"%s %s %g",dhdl,lambda,ir->init_lambda);
- setname[nsi++] = strdup(buf);
- }
- for(s=0; s<ir->n_flambda; s++)
- {
- sprintf(buf,"%s %s %g",deltag,lambda,ir->flambda[s]);
- setname[nsi++] = strdup(buf);
- }
- xvgr_legend(fp,nsets,(const char**)setname,oenv);
-
- for(s=0; s<nsets; s++)
- {
- sfree(setname[s]);
- }
- sfree(setname);
- }
-
- return fp;
-}
-
-static void copy_energy(t_mdebin *md, real e[],real ecpy[])
-{
- int i,j;
-
- for(i=j=0; (i<F_NRE); i++)
- if (md->bEner[i])
- ecpy[j++] = e[i];
- if (j != md->f_nre)
- gmx_incons("Number of energy terms wrong");
-}
-
-void upd_mdebin(t_mdebin *md, gmx_bool write_dhdl,
- gmx_bool bSum,
- double time,
- real tmass,
- gmx_enerdata_t *enerd,
- t_state *state,
- matrix box,
- tensor svir,
- tensor fvir,
- tensor vir,
- tensor pres,
- gmx_ekindata_t *ekind,
- rvec mu_tot,
- gmx_constr_t constr)
-{
- int i,j,k,kk,m,n,gid;
- real crmsd[2],tmp6[6];
- real bs[NTRICLBOXS],vol,dens,pv,enthalpy;
- real eee[egNR];
- real ecopy[F_NRE];
- real tmp;
- gmx_bool bNoseHoover;
-
- /* Do NOT use the box in the state variable, but the separate box provided
- * as an argument. This is because we sometimes need to write the box from
- * the last timestep to match the trajectory frames.
- */
- copy_energy(md, enerd->term,ecopy);
- add_ebin(md->ebin,md->ie,md->f_nre,ecopy,bSum);
- if (md->nCrmsd)
- {
- crmsd[0] = constr_rmsd(constr,FALSE);
- if (md->nCrmsd > 1)
- {
- crmsd[1] = constr_rmsd(constr,TRUE);
- }
- add_ebin(md->ebin,md->iconrmsd,md->nCrmsd,crmsd,FALSE);
- }
- if (md->bDynBox)
- {
- int nboxs;
- if(md->bTricl)
- {
- bs[0] = box[XX][XX];
- bs[1] = box[YY][YY];
- bs[2] = box[ZZ][ZZ];
- bs[3] = box[YY][XX];
- bs[4] = box[ZZ][XX];
- bs[5] = box[ZZ][YY];
- nboxs=NTRICLBOXS;
- }
- else
- {
- bs[0] = box[XX][XX];
- bs[1] = box[YY][YY];
- bs[2] = box[ZZ][ZZ];
- nboxs=NBOXS;
- }
- vol = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
- dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
-
- /* This is pV (in kJ/mol). The pressure is the reference pressure,
- not the instantaneous pressure */
- pv = 0;
- for (i=0;i<DIM;i++)
- {
- for (j=0;j<DIM;j++)
- {
- if (i>j)
- {
- pv += box[i][j]*md->ref_p[i][j]/PRESFAC;
- }
- else
- {
- pv += box[j][i]*md->ref_p[j][i]/PRESFAC;
- }
- }
- }
-
- add_ebin(md->ebin,md->ib ,nboxs,bs ,bSum);
- add_ebin(md->ebin,md->ivol ,1 ,&vol ,bSum);
- add_ebin(md->ebin,md->idens,1 ,&dens,bSum);
- add_ebin(md->ebin,md->ipv ,1 ,&pv ,bSum);
- enthalpy = pv + enerd->term[F_ETOT];
- add_ebin(md->ebin,md->ienthalpy ,1 ,&enthalpy ,bSum);
- }
- if (md->bConstrVir)
- {
- add_ebin(md->ebin,md->isvir,9,svir[0],bSum);
- add_ebin(md->ebin,md->ifvir,9,fvir[0],bSum);
- }
- add_ebin(md->ebin,md->ivir,9,vir[0],bSum);
- add_ebin(md->ebin,md->ipres,9,pres[0],bSum);
- tmp = (pres[ZZ][ZZ]-(pres[XX][XX]+pres[YY][YY])*0.5)*box[ZZ][ZZ];
- add_ebin(md->ebin,md->isurft,1,&tmp,bSum);
- if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
- {
- tmp6[0] = state->boxv[XX][XX];
- tmp6[1] = state->boxv[YY][YY];
- tmp6[2] = state->boxv[ZZ][ZZ];
- tmp6[3] = state->boxv[YY][XX];
- tmp6[4] = state->boxv[ZZ][XX];
- tmp6[5] = state->boxv[ZZ][YY];
- add_ebin(md->ebin,md->ipc,md->bTricl ? 6 : 3,tmp6,bSum);
- }
- add_ebin(md->ebin,md->imu,3,mu_tot,bSum);
- if (ekind && ekind->cosacc.cos_accel != 0)
- {
- vol = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
- dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
- add_ebin(md->ebin,md->ivcos,1,&(ekind->cosacc.vcos),bSum);
- /* 1/viscosity, unit 1/(kg m^-1 s^-1) */
- tmp = 1/(ekind->cosacc.cos_accel/(ekind->cosacc.vcos*PICO)
- *vol*sqr(box[ZZ][ZZ]*NANO/(2*M_PI)));
- add_ebin(md->ebin,md->ivisc,1,&tmp,bSum);
- }
- if (md->nE > 1)
- {
- n=0;
- for(i=0; (i<md->nEg); i++)
- {
- for(j=i; (j<md->nEg); j++)
- {
- gid=GID(i,j,md->nEg);
- for(k=kk=0; (k<egNR); k++)
- {
- if (md->bEInd[k])
- {
- eee[kk++] = enerd->grpp.ener[k][gid];
- }
- }
- add_ebin(md->ebin,md->igrp[n],md->nEc,eee,bSum);
- n++;
- }
- }
- }
-
- if (ekind)
- {
- for(i=0; (i<md->nTC); i++)
- {
- md->tmp_r[i] = ekind->tcstat[i].T;
- }
- add_ebin(md->ebin,md->itemp,md->nTC,md->tmp_r,bSum);
-
- /* whether to print Nose-Hoover chains: */
- bNoseHoover = (getenv("GMX_NOSEHOOVER_CHAINS") != NULL);
-
- if (md->etc == etcNOSEHOOVER)
- {
- if (bNoseHoover)
- {
- if (md->bNHC_trotter)
- {
- for(i=0; (i<md->nTC); i++)
- {
- for (j=0;j<md->nNHC;j++)
- {
- k = i*md->nNHC+j;
- md->tmp_r[2*k] = state->nosehoover_xi[k];
- md->tmp_r[2*k+1] = state->nosehoover_vxi[k];
- }
- }
- add_ebin(md->ebin,md->itc,md->mde_n,md->tmp_r,bSum);
-
- if (md->bMTTK) {
- for(i=0; (i<md->nTCP); i++)
- {
- for (j=0;j<md->nNHC;j++)
- {
- k = i*md->nNHC+j;
- md->tmp_r[2*k] = state->nhpres_xi[k];
- md->tmp_r[2*k+1] = state->nhpres_vxi[k];
- }
- }
- add_ebin(md->ebin,md->itcb,md->mdeb_n,md->tmp_r,bSum);
- }
-
- }
- else
- {
- for(i=0; (i<md->nTC); i++)
- {
- md->tmp_r[2*i] = state->nosehoover_xi[i];
- md->tmp_r[2*i+1] = state->nosehoover_vxi[i];
- }
- add_ebin(md->ebin,md->itc,md->mde_n,md->tmp_r,bSum);
- }
- }
- }
- else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
- md->etc == etcVRESCALE)
- {
- for(i=0; (i<md->nTC); i++)
- {
- md->tmp_r[i] = ekind->tcstat[i].lambda;
- }
- add_ebin(md->ebin,md->itc,md->nTC,md->tmp_r,bSum);
- }
- }
-
- if (ekind && md->nU > 1)
- {
- for(i=0; (i<md->nU); i++)
- {
- copy_rvec(ekind->grpstat[i].u,md->tmp_v[i]);
- }
- add_ebin(md->ebin,md->iu,3*md->nU,md->tmp_v[0],bSum);
- }
-
- ebin_increase_count(md->ebin,bSum);
-
- /* BAR + thermodynamic integration values */
- if (write_dhdl)
- {
- if (md->fp_dhdl)
- {
- fprintf(md->fp_dhdl,"%.4f", time);
-
- if (md->dhdl_derivatives)
- {
- fprintf(md->fp_dhdl," %g", enerd->term[F_DVDL]+
- enerd->term[F_DKDL]+
- enerd->term[F_DHDL_CON]);
- }
- for(i=1; i<enerd->n_lambda; i++)
- {
- fprintf(md->fp_dhdl," %g",
- enerd->enerpart_lambda[i]-enerd->enerpart_lambda[0]);
- }
- fprintf(md->fp_dhdl,"\n");
- }
- /* and the binary BAR output */
- if (md->dhc)
- {
- mde_delta_h_coll_add_dh(md->dhc,
- enerd->term[F_DVDL]+ enerd->term[F_DKDL]+
- enerd->term[F_DHDL_CON],
- enerd->enerpart_lambda, time,
- state->lambda);
- }
- }
-}
-
-void upd_mdebin_step(t_mdebin *md)
-{
- ebin_increase_count(md->ebin,FALSE);
-}
-
-static void npr(FILE *log,int n,char c)
-{
- for(; (n>0); n--) fprintf(log,"%c",c);
-}
-
-static void pprint(FILE *log,const char *s,t_mdebin *md)
-{
- char CHAR='#';
- int slen;
- char buf1[22],buf2[22];
-
- slen = strlen(s);
- fprintf(log,"\t<====== ");
- npr(log,slen,CHAR);
- fprintf(log," ==>\n");
- fprintf(log,"\t<==== %s ====>\n",s);
- fprintf(log,"\t<== ");
- npr(log,slen,CHAR);
- fprintf(log," ======>\n\n");
-
- fprintf(log,"\tStatistics over %s steps using %s frames\n",
- gmx_step_str(md->ebin->nsteps_sim,buf1),
- gmx_step_str(md->ebin->nsum_sim,buf2));
- fprintf(log,"\n");
-}
-
-void print_ebin_header(FILE *log,gmx_large_int_t steps,double time,real lamb)
-{
- char buf[22];
-
- fprintf(log," %12s %12s %12s\n"
- " %12s %12.5f %12.5f\n\n",
- "Step","Time","Lambda",gmx_step_str(steps,buf),time,lamb);
-}
-
-void print_ebin(ener_file_t fp_ene,gmx_bool bEne,gmx_bool bDR,gmx_bool bOR,
- FILE *log,
- gmx_large_int_t step,double time,
- int mode,gmx_bool bCompact,
- t_mdebin *md,t_fcdata *fcd,
- gmx_groups_t *groups,t_grpopts *opts)
-{
- /*static char **grpnms=NULL;*/
- char buf[246];
- int i,j,n,ni,nj,ndr,nor,b;
- int ndisre=0;
- real *disre_rm3tav, *disre_rt;
-
- /* these are for the old-style blocks (1 subblock, only reals), because
- there can be only one per ID for these */
- int nr[enxNR];
- int id[enxNR];
- real *block[enxNR];
-
- /* temporary arrays for the lambda values to write out */
- double enxlambda_data[2];
-
- t_enxframe fr;
-
- switch (mode)
- {
- case eprNORMAL:
- init_enxframe(&fr);
- fr.t = time;
- fr.step = step;
- fr.nsteps = md->ebin->nsteps;
- fr.dt = md->delta_t;
- fr.nsum = md->ebin->nsum;
- fr.nre = (bEne) ? md->ebin->nener : 0;
- fr.ener = md->ebin->e;
- ndisre = bDR ? fcd->disres.npair : 0;
- disre_rm3tav = fcd->disres.rm3tav;
- disre_rt = fcd->disres.rt;
- /* Optional additional old-style (real-only) blocks. */
- for(i=0; i<enxNR; i++)
- {
- nr[i] = 0;
- }
- if (fcd->orires.nr > 0 && bOR)
- {
- diagonalize_orires_tensors(&(fcd->orires));
- nr[enxOR] = fcd->orires.nr;
- block[enxOR] = fcd->orires.otav;
- id[enxOR] = enxOR;
- nr[enxORI] = (fcd->orires.oinsl != fcd->orires.otav) ?
- fcd->orires.nr : 0;
- block[enxORI] = fcd->orires.oinsl;
- id[enxORI] = enxORI;
- nr[enxORT] = fcd->orires.nex*12;
- block[enxORT] = fcd->orires.eig;
- id[enxORT] = enxORT;
- }
-
- /* whether we are going to wrte anything out: */
- if (fr.nre || ndisre || nr[enxOR] || nr[enxORI])
- {
-
- /* the old-style blocks go first */
- fr.nblock = 0;
- for(i=0; i<enxNR; i++)
- {
- if (nr[i] > 0)
- {
- fr.nblock = i + 1;
- }
- }
- add_blocks_enxframe(&fr, fr.nblock);
- for(b=0;b<fr.nblock;b++)
- {
- add_subblocks_enxblock(&(fr.block[b]), 1);
- fr.block[b].id=id[b];
- fr.block[b].sub[0].nr = nr[b];
-#ifndef GMX_DOUBLE
- fr.block[b].sub[0].type = xdr_datatype_float;
- fr.block[b].sub[0].fval = block[b];
-#else
- fr.block[b].sub[0].type = xdr_datatype_double;
- fr.block[b].sub[0].dval = block[b];
-#endif
- }
-
- /* check for disre block & fill it. */
- if (ndisre>0)
- {
- int db = fr.nblock;
- fr.nblock+=1;
- add_blocks_enxframe(&fr, fr.nblock);
-
- add_subblocks_enxblock(&(fr.block[db]), 2);
- fr.block[db].id=enxDISRE;
- fr.block[db].sub[0].nr=ndisre;
- fr.block[db].sub[1].nr=ndisre;
-#ifndef GMX_DOUBLE
- fr.block[db].sub[0].type=xdr_datatype_float;
- fr.block[db].sub[1].type=xdr_datatype_float;
- fr.block[db].sub[0].fval=disre_rt;
- fr.block[db].sub[1].fval=disre_rm3tav;
-#else
- fr.block[db].sub[0].type=xdr_datatype_double;
- fr.block[db].sub[1].type=xdr_datatype_double;
- fr.block[db].sub[0].dval=disre_rt;
- fr.block[db].sub[1].dval=disre_rm3tav;
-#endif
- }
- /* here we can put new-style blocks */
-
- /* Free energy perturbation blocks */
- if (md->dhc)
- {
- mde_delta_h_coll_handle_block(md->dhc, &fr, fr.nblock);
- }
-
- /* do the actual I/O */
- do_enx(fp_ene,&fr);
- gmx_fio_check_file_position(enx_file_pointer(fp_ene));
- if (fr.nre)
- {
- /* We have stored the sums, so reset the sum history */
- reset_ebin_sums(md->ebin);
- }
-
- /* we can now free & reset the data in the blocks */
- if (md->dhc)
- mde_delta_h_coll_reset(md->dhc);
- }
- free_enxframe(&fr);
- break;
- case eprAVER:
- if (log)
- {
- pprint(log,"A V E R A G E S",md);
- }
- break;
- case eprRMS:
- if (log)
- {
- pprint(log,"R M S - F L U C T U A T I O N S",md);
- }
- break;
- default:
- gmx_fatal(FARGS,"Invalid print mode (%d)",mode);
- }
-
- if (log)
- {
- for(i=0;i<opts->ngtc;i++)
- {
- if(opts->annealing[i]!=eannNO)
- {
- fprintf(log,"Current ref_t for group %s: %8.1f\n",
- *(groups->grpname[groups->grps[egcTC].nm_ind[i]]),
- opts->ref_t[i]);
- }
- }
- if (mode==eprNORMAL && fcd->orires.nr>0)
- {
- print_orires_log(log,&(fcd->orires));
- }
- fprintf(log," Energies (%s)\n",unit_energy);
- pr_ebin(log,md->ebin,md->ie,md->f_nre+md->nCrmsd,5,mode,TRUE);
- fprintf(log,"\n");
-
- if (!bCompact)
- {
- if (md->bDynBox)
- {
- pr_ebin(log,md->ebin,md->ib, md->bTricl ? NTRICLBOXS : NBOXS,5,
- mode,TRUE);
- fprintf(log,"\n");
- }
- if (md->bConstrVir)
- {
- fprintf(log," Constraint Virial (%s)\n",unit_energy);
- pr_ebin(log,md->ebin,md->isvir,9,3,mode,FALSE);
- fprintf(log,"\n");
- fprintf(log," Force Virial (%s)\n",unit_energy);
- pr_ebin(log,md->ebin,md->ifvir,9,3,mode,FALSE);
- fprintf(log,"\n");
- }
- fprintf(log," Total Virial (%s)\n",unit_energy);
- pr_ebin(log,md->ebin,md->ivir,9,3,mode,FALSE);
- fprintf(log,"\n");
- fprintf(log," Pressure (%s)\n",unit_pres_bar);
- pr_ebin(log,md->ebin,md->ipres,9,3,mode,FALSE);
- fprintf(log,"\n");
- fprintf(log," Total Dipole (%s)\n",unit_dipole_D);
- pr_ebin(log,md->ebin,md->imu,3,3,mode,FALSE);
- fprintf(log,"\n");
-
- if (md->nE > 1)
- {
- if (md->print_grpnms==NULL)
- {
- snew(md->print_grpnms,md->nE);
- n=0;
- for(i=0; (i<md->nEg); i++)
- {
- ni=groups->grps[egcENER].nm_ind[i];
- for(j=i; (j<md->nEg); j++)
- {
- nj=groups->grps[egcENER].nm_ind[j];
- sprintf(buf,"%s-%s",*(groups->grpname[ni]),
- *(groups->grpname[nj]));
- md->print_grpnms[n++]=strdup(buf);
- }
- }
- }
- sprintf(buf,"Epot (%s)",unit_energy);
- fprintf(log,"%15s ",buf);
- for(i=0; (i<egNR); i++)
- {
- if (md->bEInd[i])
- {
- fprintf(log,"%12s ",egrp_nm[i]);
- }
- }
- fprintf(log,"\n");
- for(i=0; (i<md->nE); i++)
- {
- fprintf(log,"%15s",md->print_grpnms[i]);
- pr_ebin(log,md->ebin,md->igrp[i],md->nEc,md->nEc,mode,
- FALSE);
- }
- fprintf(log,"\n");
- }
- if (md->nTC > 1)
- {
- pr_ebin(log,md->ebin,md->itemp,md->nTC,4,mode,TRUE);
- fprintf(log,"\n");
- }
- if (md->nU > 1)
- {
- fprintf(log,"%15s %12s %12s %12s\n",
- "Group","Ux","Uy","Uz");
- for(i=0; (i<md->nU); i++)
- {
- ni=groups->grps[egcACC].nm_ind[i];
- fprintf(log,"%15s",*groups->grpname[ni]);
- pr_ebin(log,md->ebin,md->iu+3*i,3,3,mode,FALSE);
- }
- fprintf(log,"\n");
- }
- }
- }
-
-}
-
-void update_energyhistory(energyhistory_t * enerhist,t_mdebin * mdebin)
-{
- int i;
-
- enerhist->nsteps = mdebin->ebin->nsteps;
- enerhist->nsum = mdebin->ebin->nsum;
- enerhist->nsteps_sim = mdebin->ebin->nsteps_sim;
- enerhist->nsum_sim = mdebin->ebin->nsum_sim;
- enerhist->nener = mdebin->ebin->nener;
-
- if (mdebin->ebin->nsum > 0)
- {
- /* Check if we need to allocate first */
- if(enerhist->ener_ave == NULL)
- {
- snew(enerhist->ener_ave,enerhist->nener);
- snew(enerhist->ener_sum,enerhist->nener);
- }
-
- for(i=0;i<enerhist->nener;i++)
- {
- enerhist->ener_ave[i] = mdebin->ebin->e[i].eav;
- enerhist->ener_sum[i] = mdebin->ebin->e[i].esum;
- }
- }
-
- if (mdebin->ebin->nsum_sim > 0)
- {
- /* Check if we need to allocate first */
- if(enerhist->ener_sum_sim == NULL)
- {
- snew(enerhist->ener_sum_sim,enerhist->nener);
- }
-
- for(i=0;i<enerhist->nener;i++)
- {
- enerhist->ener_sum_sim[i] = mdebin->ebin->e_sim[i].esum;
- }
- }
- if (mdebin->dhc)
- {
- mde_delta_h_coll_update_energyhistory(mdebin->dhc, enerhist);
- }
-}
-
-void restore_energyhistory_from_state(t_mdebin * mdebin,
- energyhistory_t * enerhist)
-{
- int i;
-
- if ((enerhist->nsum > 0 || enerhist->nsum_sim > 0) &&
- mdebin->ebin->nener != enerhist->nener)
- {
- gmx_fatal(FARGS,"Mismatch between number of energies in run input (%d) and checkpoint file (%d).",
- mdebin->ebin->nener,enerhist->nener);
- }
-
- mdebin->ebin->nsteps = enerhist->nsteps;
- mdebin->ebin->nsum = enerhist->nsum;
- mdebin->ebin->nsteps_sim = enerhist->nsteps_sim;
- mdebin->ebin->nsum_sim = enerhist->nsum_sim;
-
- for(i=0; i<mdebin->ebin->nener; i++)
- {
- mdebin->ebin->e[i].eav =
- (enerhist->nsum > 0 ? enerhist->ener_ave[i] : 0);
- mdebin->ebin->e[i].esum =
- (enerhist->nsum > 0 ? enerhist->ener_sum[i] : 0);
- mdebin->ebin->e_sim[i].esum =
- (enerhist->nsum_sim > 0 ? enerhist->ener_sum_sim[i] : 0);
- }
- if (mdebin->dhc)
- {
- mde_delta_h_coll_restore_energyhistory(mdebin->dhc, enerhist);
- }
-}
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROwing Monsters And Cloning Shrimps
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <time.h>
-#include <math.h>
-#include "sysstuff.h"
-#include "string2.h"
-#include "network.h"
-#include "confio.h"
-#include "copyrite.h"
-#include "smalloc.h"
-#include "nrnb.h"
-#include "main.h"
-#include "force.h"
-#include "macros.h"
-#include "random.h"
-#include "names.h"
-#include "gmx_fatal.h"
-#include "txtdump.h"
-#include "typedefs.h"
-#include "update.h"
-#include "constr.h"
-#include "vec.h"
-#include "statutil.h"
-#include "tgroup.h"
-#include "mdebin.h"
-#include "vsite.h"
-#include "force.h"
-#include "mdrun.h"
-#include "domdec.h"
-#include "partdec.h"
-#include "trnio.h"
-#include "sparsematrix.h"
-#include "mtxio.h"
-#include "mdatoms.h"
-#include "ns.h"
-#include "gmx_wallcycle.h"
-#include "mtop_util.h"
-#include "gmxfio.h"
-#include "pme.h"
-
-typedef struct {
- t_state s;
- rvec *f;
- real epot;
- real fnorm;
- real fmax;
- int a_fmax;
-} em_state_t;
-
-static em_state_t *init_em_state()
-{
- em_state_t *ems;
-
- snew(ems,1);
-
- return ems;
-}
-
-static void print_em_start(FILE *fplog,t_commrec *cr,gmx_runtime_t *runtime,
- gmx_wallcycle_t wcycle,
- const char *name)
-{
- char buf[STRLEN];
-
- runtime_start(runtime);
-
- sprintf(buf,"Started %s",name);
- print_date_and_time(fplog,cr->nodeid,buf,NULL);
-
- wallcycle_start(wcycle,ewcRUN);
-}
-static void em_time_end(FILE *fplog,t_commrec *cr,gmx_runtime_t *runtime,
- gmx_wallcycle_t wcycle)
-{
- wallcycle_stop(wcycle,ewcRUN);
-
- runtime_end(runtime);
-}
-
-static void sp_header(FILE *out,const char *minimizer,real ftol,int nsteps)
-{
- fprintf(out,"\n");
- fprintf(out,"%s:\n",minimizer);
- fprintf(out," Tolerance (Fmax) = %12.5e\n",ftol);
- fprintf(out," Number of steps = %12d\n",nsteps);
-}
-
-static void warn_step(FILE *fp,real ftol,gmx_bool bLastStep,gmx_bool bConstrain)
-{
- if (bLastStep)
- {
- fprintf(fp,"\nReached the maximum number of steps before reaching Fmax < %g\n",ftol);
- }
- else
- {
- fprintf(fp,"\nStepsize too small, or no change in energy.\n"
- "Converged to machine precision,\n"
- "but not to the requested precision Fmax < %g\n",
- ftol);
- if (sizeof(real)<sizeof(double))
- {
- fprintf(fp,"\nDouble precision normally gives you higher accuracy.\n");
- }
- if (bConstrain)
- {
- fprintf(fp,"You might need to increase your constraint accuracy, or turn\n"
- "off constraints alltogether (set constraints = none in mdp file)\n");
- }
- }
-}
-
-
-
-static void print_converged(FILE *fp,const char *alg,real ftol,
- gmx_large_int_t count,gmx_bool bDone,gmx_large_int_t nsteps,
- real epot,real fmax, int nfmax, real fnorm)
-{
- char buf[STEPSTRSIZE];
-
- if (bDone)
- fprintf(fp,"\n%s converged to Fmax < %g in %s steps\n",
- alg,ftol,gmx_step_str(count,buf));
- else if(count<nsteps)
- fprintf(fp,"\n%s converged to machine precision in %s steps,\n"
- "but did not reach the requested Fmax < %g.\n",
- alg,gmx_step_str(count,buf),ftol);
- else
- fprintf(fp,"\n%s did not converge to Fmax < %g in %s steps.\n",
- alg,ftol,gmx_step_str(count,buf));
-
-#ifdef GMX_DOUBLE
- fprintf(fp,"Potential Energy = %21.14e\n",epot);
- fprintf(fp,"Maximum force = %21.14e on atom %d\n",fmax,nfmax+1);
- fprintf(fp,"Norm of force = %21.14e\n",fnorm);
-#else
- fprintf(fp,"Potential Energy = %14.7e\n",epot);
- fprintf(fp,"Maximum force = %14.7e on atom %d\n",fmax,nfmax+1);
- fprintf(fp,"Norm of force = %14.7e\n",fnorm);
-#endif
-}
-
-static void get_f_norm_max(t_commrec *cr,
- t_grpopts *opts,t_mdatoms *mdatoms,rvec *f,
- real *fnorm,real *fmax,int *a_fmax)
-{
- double fnorm2,*sum;
- real fmax2,fmax2_0,fam;
- int la_max,a_max,start,end,i,m,gf;
-
- /* This routine finds the largest force and returns it.
- * On parallel machines the global max is taken.
- */
- fnorm2 = 0;
- fmax2 = 0;
- la_max = -1;
- gf = 0;
- start = mdatoms->start;
- end = mdatoms->homenr + start;
- if (mdatoms->cFREEZE) {
- for(i=start; i<end; i++) {
- gf = mdatoms->cFREEZE[i];
- fam = 0;
- for(m=0; m<DIM; m++)
- if (!opts->nFreeze[gf][m])
- fam += sqr(f[i][m]);
- fnorm2 += fam;
- if (fam > fmax2) {
- fmax2 = fam;
- la_max = i;
- }
- }
- } else {
- for(i=start; i<end; i++) {
- fam = norm2(f[i]);
- fnorm2 += fam;
- if (fam > fmax2) {
- fmax2 = fam;
- la_max = i;
- }
- }
- }
-
- if (la_max >= 0 && DOMAINDECOMP(cr)) {
- a_max = cr->dd->gatindex[la_max];
- } else {
- a_max = la_max;
- }
- if (PAR(cr)) {
- snew(sum,2*cr->nnodes+1);
- sum[2*cr->nodeid] = fmax2;
- sum[2*cr->nodeid+1] = a_max;
- sum[2*cr->nnodes] = fnorm2;
- gmx_sumd(2*cr->nnodes+1,sum,cr);
- fnorm2 = sum[2*cr->nnodes];
- /* Determine the global maximum */
- for(i=0; i<cr->nnodes; i++) {
- if (sum[2*i] > fmax2) {
- fmax2 = sum[2*i];
- a_max = (int)(sum[2*i+1] + 0.5);
- }
- }
- sfree(sum);
- }
-
- if (fnorm)
- *fnorm = sqrt(fnorm2);
- if (fmax)
- *fmax = sqrt(fmax2);
- if (a_fmax)
- *a_fmax = a_max;
-}
-
-static void get_state_f_norm_max(t_commrec *cr,
- t_grpopts *opts,t_mdatoms *mdatoms,
- em_state_t *ems)
-{
- get_f_norm_max(cr,opts,mdatoms,ems->f,&ems->fnorm,&ems->fmax,&ems->a_fmax);
-}
-
-void init_em(FILE *fplog,const char *title,
- t_commrec *cr,t_inputrec *ir,
- t_state *state_global,gmx_mtop_t *top_global,
- em_state_t *ems,gmx_localtop_t **top,
- rvec **f,rvec **f_global,
- t_nrnb *nrnb,rvec mu_tot,
- t_forcerec *fr,gmx_enerdata_t **enerd,
- t_graph **graph,t_mdatoms *mdatoms,gmx_global_stat_t *gstat,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int nfile,const t_filenm fnm[],
- gmx_mdoutf_t **outf,t_mdebin **mdebin)
-{
- int start,homenr,i;
- real dvdlambda;
-
- if (fplog)
- {
- fprintf(fplog,"Initiating %s\n",title);
- }
-
- state_global->ngtc = 0;
-
- /* Initiate some variables */
- if (ir->efep != efepNO)
- {
- state_global->lambda = ir->init_lambda;
- }
- else
- {
- state_global->lambda = 0.0;
- }
-
- init_nrnb(nrnb);
-
- if (DOMAINDECOMP(cr))
- {
- *top = dd_init_local_top(top_global);
-
- dd_init_local_state(cr->dd,state_global,&ems->s);
-
- *f = NULL;
-
- /* Distribute the charge groups over the nodes from the master node */
- dd_partition_system(fplog,ir->init_step,cr,TRUE,1,
- state_global,top_global,ir,
- &ems->s,&ems->f,mdatoms,*top,
- fr,vsite,NULL,constr,
- nrnb,NULL,FALSE);
- dd_store_state(cr->dd,&ems->s);
-
- if (ir->nstfout)
- {
- snew(*f_global,top_global->natoms);
- }
- else
- {
- *f_global = NULL;
- }
- *graph = NULL;
- }
- else
- {
- snew(*f,top_global->natoms);
-
- /* Just copy the state */
- ems->s = *state_global;
- snew(ems->s.x,ems->s.nalloc);
- snew(ems->f,ems->s.nalloc);
- for(i=0; i<state_global->natoms; i++)
- {
- copy_rvec(state_global->x[i],ems->s.x[i]);
- }
- copy_mat(state_global->box,ems->s.box);
-
- if (PAR(cr) && ir->eI != eiNM)
- {
- /* Initialize the particle decomposition and split the topology */
- *top = split_system(fplog,top_global,ir,cr);
-
- pd_cg_range(cr,&fr->cg0,&fr->hcg);
- }
- else
- {
- *top = gmx_mtop_generate_local_top(top_global,ir);
- }
- *f_global = *f;
-
- if (ir->ePBC != epbcNONE && !ir->bPeriodicMols)
- {
- *graph = mk_graph(fplog,&((*top)->idef),0,top_global->natoms,FALSE,FALSE);
- }
- else
- {
- *graph = NULL;
- }
-
- if (PARTDECOMP(cr))
- {
- pd_at_range(cr,&start,&homenr);
- homenr -= start;
- }
- else
- {
- start = 0;
- homenr = top_global->natoms;
- }
- atoms2md(top_global,ir,0,NULL,start,homenr,mdatoms);
- update_mdatoms(mdatoms,state_global->lambda);
-
- if (vsite)
- {
- set_vsite_top(vsite,*top,mdatoms,cr);
- }
- }
-
- if (constr)
- {
- if (ir->eConstrAlg == econtSHAKE &&
- gmx_mtop_ftype_count(top_global,F_CONSTR) > 0)
- {
- gmx_fatal(FARGS,"Can not do energy minimization with %s, use %s\n",
- econstr_names[econtSHAKE],econstr_names[econtLINCS]);
- }
-
- if (!DOMAINDECOMP(cr))
- {
- set_constraints(constr,*top,ir,mdatoms,cr);
- }
-
- if (!ir->bContinuation)
- {
- /* Constrain the starting coordinates */
- dvdlambda=0;
- constrain(PAR(cr) ? NULL : fplog,TRUE,TRUE,constr,&(*top)->idef,
- ir,NULL,cr,-1,0,mdatoms,
- ems->s.x,ems->s.x,NULL,ems->s.box,
- ems->s.lambda,&dvdlambda,
- NULL,NULL,nrnb,econqCoord,FALSE,0,0);
- }
- }
-
- if (PAR(cr))
- {
- *gstat = global_stat_init(ir);
- }
-
- *outf = init_mdoutf(nfile,fnm,0,cr,ir,NULL);
-
- snew(*enerd,1);
- init_enerdata(top_global->groups.grps[egcENER].nr,ir->n_flambda,*enerd);
-
- if (mdebin != NULL)
- {
- /* Init bin for energy stuff */
- *mdebin = init_mdebin((*outf)->fp_ene,top_global,ir,NULL);
- }
-
- clear_rvec(mu_tot);
- calc_shifts(ems->s.box,fr->shift_vec);
-}
-
-static void finish_em(FILE *fplog,t_commrec *cr,gmx_mdoutf_t *outf,
- gmx_runtime_t *runtime,gmx_wallcycle_t wcycle)
-{
- if (!(cr->duty & DUTY_PME)) {
- /* Tell the PME only node to finish */
- gmx_pme_finish(cr);
- }
-
- done_mdoutf(outf);
-
- em_time_end(fplog,cr,runtime,wcycle);
-}
-
-static void swap_em_state(em_state_t *ems1,em_state_t *ems2)
-{
- em_state_t tmp;
-
- tmp = *ems1;
- *ems1 = *ems2;
- *ems2 = tmp;
-}
-
-static void copy_em_coords_back(em_state_t *ems,t_state *state,rvec *f)
-{
- int i;
-
- for(i=0; (i<state->natoms); i++)
- copy_rvec(ems->s.x[i],state->x[i]);
- if (f != NULL)
- copy_rvec(ems->f[i],f[i]);
-}
-
-static void write_em_traj(FILE *fplog,t_commrec *cr,
- gmx_mdoutf_t *outf,
- gmx_bool bX,gmx_bool bF,const char *confout,
- gmx_mtop_t *top_global,
- t_inputrec *ir,gmx_large_int_t step,
- em_state_t *state,
- t_state *state_global,rvec *f_global)
-{
- int mdof_flags;
-
- if ((bX || bF || confout != NULL) && !DOMAINDECOMP(cr))
- {
- f_global = state->f;
- copy_em_coords_back(state,state_global,bF ? f_global : NULL);
- }
-
- mdof_flags = 0;
- if (bX) { mdof_flags |= MDOF_X; }
- if (bF) { mdof_flags |= MDOF_F; }
- write_traj(fplog,cr,outf,mdof_flags,
- top_global,step,(double)step,
- &state->s,state_global,state->f,f_global,NULL,NULL);
-
- if (confout != NULL && MASTER(cr))
- {
- if (ir->ePBC != epbcNONE && !ir->bPeriodicMols && DOMAINDECOMP(cr))
- {
- /* Make molecules whole only for confout writing */
- do_pbc_mtop(fplog,ir->ePBC,state_global->box,top_global,
- state_global->x);
- }
-
- write_sto_conf_mtop(confout,
- *top_global->name,top_global,
- state_global->x,NULL,ir->ePBC,state_global->box);
- }
-}
-
-static void do_em_step(t_commrec *cr,t_inputrec *ir,t_mdatoms *md,
- em_state_t *ems1,real a,rvec *f,em_state_t *ems2,
- gmx_constr_t constr,gmx_localtop_t *top,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_large_int_t count)
-
-{
- t_state *s1,*s2;
- int start,end,gf,i,m;
- rvec *x1,*x2;
- real dvdlambda;
-
- s1 = &ems1->s;
- s2 = &ems2->s;
-
- if (DOMAINDECOMP(cr) && s1->ddp_count != cr->dd->ddp_count)
- gmx_incons("state mismatch in do_em_step");
-
- s2->flags = s1->flags;
-
- if (s2->nalloc != s1->nalloc) {
- s2->nalloc = s1->nalloc;
- srenew(s2->x,s1->nalloc);
- srenew(ems2->f, s1->nalloc);
- if (s2->flags & (1<<estCGP))
- srenew(s2->cg_p, s1->nalloc);
- }
-
- s2->natoms = s1->natoms;
- s2->lambda = s1->lambda;
- copy_mat(s1->box,s2->box);
-
- start = md->start;
- end = md->start + md->homenr;
-
- x1 = s1->x;
- x2 = s2->x;
- gf = 0;
- for(i=start; i<end; i++) {
- if (md->cFREEZE)
- gf = md->cFREEZE[i];
- for(m=0; m<DIM; m++) {
- if (ir->opts.nFreeze[gf][m])
- x2[i][m] = x1[i][m];
- else
- x2[i][m] = x1[i][m] + a*f[i][m];
- }
- }
-
- if (s2->flags & (1<<estCGP)) {
- /* Copy the CG p vector */
- x1 = s1->cg_p;
- x2 = s2->cg_p;
- for(i=start; i<end; i++)
- copy_rvec(x1[i],x2[i]);
- }
-
- if (DOMAINDECOMP(cr)) {
- s2->ddp_count = s1->ddp_count;
- if (s2->cg_gl_nalloc < s1->cg_gl_nalloc) {
- s2->cg_gl_nalloc = s1->cg_gl_nalloc;
- srenew(s2->cg_gl,s2->cg_gl_nalloc);
- }
- s2->ncg_gl = s1->ncg_gl;
- for(i=0; i<s2->ncg_gl; i++)
- s2->cg_gl[i] = s1->cg_gl[i];
- s2->ddp_count_cg_gl = s1->ddp_count_cg_gl;
- }
-
- if (constr) {
- wallcycle_start(wcycle,ewcCONSTR);
- dvdlambda = 0;
- constrain(NULL,TRUE,TRUE,constr,&top->idef,
- ir,NULL,cr,count,0,md,
- s1->x,s2->x,NULL,s2->box,s2->lambda,
- &dvdlambda,NULL,NULL,nrnb,econqCoord,FALSE,0,0);
- wallcycle_stop(wcycle,ewcCONSTR);
- }
-}
-
-static void do_x_step(t_commrec *cr,int n,rvec *x1,real a,rvec *f,rvec *x2)
-
-{
- int start,end,i,m;
-
- if (DOMAINDECOMP(cr)) {
- start = 0;
- end = cr->dd->nat_home;
- } else if (PARTDECOMP(cr)) {
- pd_at_range(cr,&start,&end);
- } else {
- start = 0;
- end = n;
- }
-
- for(i=start; i<end; i++) {
- for(m=0; m<DIM; m++) {
- x2[i][m] = x1[i][m] + a*f[i][m];
- }
- }
-}
-
-static void do_x_sub(t_commrec *cr,int n,rvec *x1,rvec *x2,real a,rvec *f)
-
-{
- int start,end,i,m;
-
- if (DOMAINDECOMP(cr)) {
- start = 0;
- end = cr->dd->nat_home;
- } else if (PARTDECOMP(cr)) {
- pd_at_range(cr,&start,&end);
- } else {
- start = 0;
- end = n;
- }
-
- for(i=start; i<end; i++) {
- for(m=0; m<DIM; m++) {
- f[i][m] = (x1[i][m] - x2[i][m])*a;
- }
- }
-}
-
-static void em_dd_partition_system(FILE *fplog,int step,t_commrec *cr,
- gmx_mtop_t *top_global,t_inputrec *ir,
- em_state_t *ems,gmx_localtop_t *top,
- t_mdatoms *mdatoms,t_forcerec *fr,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle)
-{
- /* Repartition the domain decomposition */
- wallcycle_start(wcycle,ewcDOMDEC);
- dd_partition_system(fplog,step,cr,FALSE,1,
- NULL,top_global,ir,
- &ems->s,&ems->f,
- mdatoms,top,fr,vsite,NULL,constr,
- nrnb,wcycle,FALSE);
- dd_store_state(cr->dd,&ems->s);
- wallcycle_stop(wcycle,ewcDOMDEC);
-}
-
-static void evaluate_energy(FILE *fplog,gmx_bool bVerbose,t_commrec *cr,
- t_state *state_global,gmx_mtop_t *top_global,
- em_state_t *ems,gmx_localtop_t *top,
- t_inputrec *inputrec,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_global_stat_t gstat,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- t_fcdata *fcd,
- t_graph *graph,t_mdatoms *mdatoms,
- t_forcerec *fr,rvec mu_tot,
- gmx_enerdata_t *enerd,tensor vir,tensor pres,
- gmx_large_int_t count,gmx_bool bFirst)
-{
- real t;
- gmx_bool bNS;
- int nabnsb;
- tensor force_vir,shake_vir,ekin;
- real dvdl,prescorr,enercorr,dvdlcorr;
- real terminate=0;
-
- /* Set the time to the initial time, the time does not change during EM */
- t = inputrec->init_t;
-
- if (bFirst ||
- (DOMAINDECOMP(cr) && ems->s.ddp_count < cr->dd->ddp_count)) {
- /* This the first state or an old state used before the last ns */
- bNS = TRUE;
- } else {
- bNS = FALSE;
- if (inputrec->nstlist > 0) {
- bNS = TRUE;
- } else if (inputrec->nstlist == -1) {
- nabnsb = natoms_beyond_ns_buffer(inputrec,fr,&top->cgs,NULL,ems->s.x);
- if (PAR(cr))
- gmx_sumi(1,&nabnsb,cr);
- bNS = (nabnsb > 0);
- }
- }
-
- if (vsite)
- construct_vsites(fplog,vsite,ems->s.x,nrnb,1,NULL,
- top->idef.iparams,top->idef.il,
- fr->ePBC,fr->bMolPBC,graph,cr,ems->s.box);
-
- if (DOMAINDECOMP(cr)) {
- if (bNS) {
- /* Repartition the domain decomposition */
- em_dd_partition_system(fplog,count,cr,top_global,inputrec,
- ems,top,mdatoms,fr,vsite,constr,
- nrnb,wcycle);
- }
- }
-
- /* Calc force & energy on new trial position */
- /* do_force always puts the charge groups in the box and shifts again
- * We do not unshift, so molecules are always whole in congrad.c
- */
- do_force(fplog,cr,inputrec,
- count,nrnb,wcycle,top,top_global,&top_global->groups,
- ems->s.box,ems->s.x,&ems->s.hist,
- ems->f,force_vir,mdatoms,enerd,fcd,
- ems->s.lambda,graph,fr,vsite,mu_tot,t,NULL,NULL,TRUE,
- GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | GMX_FORCE_VIRIAL |
- (bNS ? GMX_FORCE_NS | GMX_FORCE_DOLR : 0));
-
- /* Clear the unused shake virial and pressure */
- clear_mat(shake_vir);
- clear_mat(pres);
-
- /* Calculate long range corrections to pressure and energy */
- calc_dispcorr(fplog,inputrec,fr,count,top_global->natoms,ems->s.box,ems->s.lambda,
- pres,force_vir,&prescorr,&enercorr,&dvdlcorr);
- /* don't think these next 4 lines can be moved in for now, because we
- don't always want to write it -- figure out how to clean this up MRS 8/4/2009 */
- enerd->term[F_DISPCORR] = enercorr;
- enerd->term[F_EPOT] += enercorr;
- enerd->term[F_PRES] += prescorr;
- enerd->term[F_DVDL] += dvdlcorr;
-
- /* Communicate stuff when parallel */
- if (PAR(cr) && inputrec->eI != eiNM)
- {
- wallcycle_start(wcycle,ewcMoveE);
-
- global_stat(fplog,gstat,cr,enerd,force_vir,shake_vir,mu_tot,
- inputrec,NULL,NULL,NULL,1,&terminate,
- top_global,&ems->s,FALSE,
- CGLO_ENERGY |
- CGLO_PRESSURE |
- CGLO_CONSTRAINT |
- CGLO_FIRSTITERATE);
-
- wallcycle_stop(wcycle,ewcMoveE);
- }
-
- ems->epot = enerd->term[F_EPOT];
-
- if (constr) {
- /* Project out the constraint components of the force */
- wallcycle_start(wcycle,ewcCONSTR);
- dvdl = 0;
- constrain(NULL,FALSE,FALSE,constr,&top->idef,
- inputrec,NULL,cr,count,0,mdatoms,
- ems->s.x,ems->f,ems->f,ems->s.box,ems->s.lambda,&dvdl,
- NULL,&shake_vir,nrnb,econqForceDispl,FALSE,0,0);
- if (fr->bSepDVDL && fplog)
- fprintf(fplog,sepdvdlformat,"Constraints",t,dvdl);
- enerd->term[F_DHDL_CON] += dvdl;
- m_add(force_vir,shake_vir,vir);
- wallcycle_stop(wcycle,ewcCONSTR);
- } else {
- copy_mat(force_vir,vir);
- }
-
- clear_mat(ekin);
- enerd->term[F_PRES] =
- calc_pres(fr->ePBC,inputrec->nwall,ems->s.box,ekin,vir,pres,
- (fr->eeltype==eelPPPM)?enerd->term[F_COUL_RECIP]:0.0);
-
- sum_dhdl(enerd,ems->s.lambda,inputrec);
-
- if (EI_ENERGY_MINIMIZATION(inputrec->eI))
- {
- get_state_f_norm_max(cr,&(inputrec->opts),mdatoms,ems);
- }
-}
-
-static double reorder_partsum(t_commrec *cr,t_grpopts *opts,t_mdatoms *mdatoms,
- gmx_mtop_t *mtop,
- em_state_t *s_min,em_state_t *s_b)
-{
- rvec *fm,*fb,*fmg;
- t_block *cgs_gl;
- int ncg,*cg_gl,*index,c,cg,i,a0,a1,a,gf,m;
- double partsum;
- unsigned char *grpnrFREEZE;
-
- if (debug)
- fprintf(debug,"Doing reorder_partsum\n");
-
- fm = s_min->f;
- fb = s_b->f;
-
- cgs_gl = dd_charge_groups_global(cr->dd);
- index = cgs_gl->index;
-
- /* Collect fm in a global vector fmg.
- * This conflicts with the spirit of domain decomposition,
- * but to fully optimize this a much more complicated algorithm is required.
- */
- snew(fmg,mtop->natoms);
-
- ncg = s_min->s.ncg_gl;
- cg_gl = s_min->s.cg_gl;
- i = 0;
- for(c=0; c<ncg; c++) {
- cg = cg_gl[c];
- a0 = index[cg];
- a1 = index[cg+1];
- for(a=a0; a<a1; a++) {
- copy_rvec(fm[i],fmg[a]);
- i++;
- }
- }
- gmx_sum(mtop->natoms*3,fmg[0],cr);
-
- /* Now we will determine the part of the sum for the cgs in state s_b */
- ncg = s_b->s.ncg_gl;
- cg_gl = s_b->s.cg_gl;
- partsum = 0;
- i = 0;
- gf = 0;
- grpnrFREEZE = mtop->groups.grpnr[egcFREEZE];
- for(c=0; c<ncg; c++) {
- cg = cg_gl[c];
- a0 = index[cg];
- a1 = index[cg+1];
- for(a=a0; a<a1; a++) {
- if (mdatoms->cFREEZE && grpnrFREEZE) {
- gf = grpnrFREEZE[i];
- }
- for(m=0; m<DIM; m++) {
- if (!opts->nFreeze[gf][m]) {
- partsum += (fb[i][m] - fmg[a][m])*fb[i][m];
- }
- }
- i++;
- }
- }
-
- sfree(fmg);
-
- return partsum;
-}
-
-static real pr_beta(t_commrec *cr,t_grpopts *opts,t_mdatoms *mdatoms,
- gmx_mtop_t *mtop,
- em_state_t *s_min,em_state_t *s_b)
-{
- rvec *fm,*fb;
- double sum;
- int gf,i,m;
-
- /* This is just the classical Polak-Ribiere calculation of beta;
- * it looks a bit complicated since we take freeze groups into account,
- * and might have to sum it in parallel runs.
- */
-
- if (!DOMAINDECOMP(cr) ||
- (s_min->s.ddp_count == cr->dd->ddp_count &&
- s_b->s.ddp_count == cr->dd->ddp_count)) {
- fm = s_min->f;
- fb = s_b->f;
- sum = 0;
- gf = 0;
- /* This part of code can be incorrect with DD,
- * since the atom ordering in s_b and s_min might differ.
- */
- for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
- if (mdatoms->cFREEZE)
- gf = mdatoms->cFREEZE[i];
- for(m=0; m<DIM; m++)
- if (!opts->nFreeze[gf][m]) {
- sum += (fb[i][m] - fm[i][m])*fb[i][m];
- }
- }
- } else {
- /* We need to reorder cgs while summing */
- sum = reorder_partsum(cr,opts,mdatoms,mtop,s_min,s_b);
- }
- if (PAR(cr))
- gmx_sumd(1,&sum,cr);
-
- return sum/sqr(s_min->fnorm);
-}
-
-double do_cg(FILE *fplog,t_commrec *cr,
- int nfile,const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,
- t_inputrec *inputrec,
- gmx_mtop_t *top_global,t_fcdata *fcd,
- t_state *state_global,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,
- t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime)
-{
- const char *CG="Polak-Ribiere Conjugate Gradients";
-
- em_state_t *s_min,*s_a,*s_b,*s_c;
- gmx_localtop_t *top;
- gmx_enerdata_t *enerd;
- rvec *f;
- gmx_global_stat_t gstat;
- t_graph *graph;
- rvec *f_global,*p,*sf,*sfm;
- double gpa,gpb,gpc,tmp,sum[2],minstep;
- real fnormn;
- real stepsize;
- real a,b,c,beta=0.0;
- real epot_repl=0;
- real pnorm;
- t_mdebin *mdebin;
- gmx_bool converged,foundlower;
- rvec mu_tot;
- gmx_bool do_log=FALSE,do_ene=FALSE,do_x,do_f;
- tensor vir,pres;
- int number_steps,neval=0,nstcg=inputrec->nstcgsteep;
- gmx_mdoutf_t *outf;
- int i,m,gf,step,nminstep;
- real terminate=0;
-
- step=0;
-
- s_min = init_em_state();
- s_a = init_em_state();
- s_b = init_em_state();
- s_c = init_em_state();
-
- /* Init em and store the local state in s_min */
- init_em(fplog,CG,cr,inputrec,
- state_global,top_global,s_min,&top,&f,&f_global,
- nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
- nfile,fnm,&outf,&mdebin);
-
- /* Print to log file */
- print_em_start(fplog,cr,runtime,wcycle,CG);
-
- /* Max number of steps */
- number_steps=inputrec->nsteps;
-
- if (MASTER(cr))
- sp_header(stderr,CG,inputrec->em_tol,number_steps);
- if (fplog)
- sp_header(fplog,CG,inputrec->em_tol,number_steps);
-
- /* Call the force routine and some auxiliary (neighboursearching etc.) */
- /* do_force always puts the charge groups in the box and shifts again
- * We do not unshift, so molecules are always whole in congrad.c
- */
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,s_min,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,-1,TRUE);
- where();
-
- if (MASTER(cr)) {
- /* Copy stuff to the energy bin for easy printing etc. */
- upd_mdebin(mdebin,FALSE,FALSE,(double)step,
- mdatoms->tmass,enerd,&s_min->s,s_min->s.box,
- NULL,NULL,vir,pres,NULL,mu_tot,constr);
-
- print_ebin_header(fplog,step,step,s_min->s.lambda);
- print_ebin(outf->fp_ene,TRUE,FALSE,FALSE,fplog,step,step,eprNORMAL,
- TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
- }
- where();
-
- /* Estimate/guess the initial stepsize */
- stepsize = inputrec->em_stepsize/s_min->fnorm;
-
- if (MASTER(cr)) {
- fprintf(stderr," F-max = %12.5e on atom %d\n",
- s_min->fmax,s_min->a_fmax+1);
- fprintf(stderr," F-Norm = %12.5e\n",
- s_min->fnorm/sqrt(state_global->natoms));
- fprintf(stderr,"\n");
- /* and copy to the log file too... */
- fprintf(fplog," F-max = %12.5e on atom %d\n",
- s_min->fmax,s_min->a_fmax+1);
- fprintf(fplog," F-Norm = %12.5e\n",
- s_min->fnorm/sqrt(state_global->natoms));
- fprintf(fplog,"\n");
- }
- /* Start the loop over CG steps.
- * Each successful step is counted, and we continue until
- * we either converge or reach the max number of steps.
- */
- converged = FALSE;
- for(step=0; (number_steps<0 || (number_steps>=0 && step<=number_steps)) && !converged;step++) {
-
- /* start taking steps in a new direction
- * First time we enter the routine, beta=0, and the direction is
- * simply the negative gradient.
- */
-
- /* Calculate the new direction in p, and the gradient in this direction, gpa */
- p = s_min->s.cg_p;
- sf = s_min->f;
- gpa = 0;
- gf = 0;
- for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
- if (mdatoms->cFREEZE)
- gf = mdatoms->cFREEZE[i];
- for(m=0; m<DIM; m++) {
- if (!inputrec->opts.nFreeze[gf][m]) {
- p[i][m] = sf[i][m] + beta*p[i][m];
- gpa -= p[i][m]*sf[i][m];
- /* f is negative gradient, thus the sign */
- } else {
- p[i][m] = 0;
- }
- }
- }
-
- /* Sum the gradient along the line across CPUs */
- if (PAR(cr))
- gmx_sumd(1,&gpa,cr);
-
- /* Calculate the norm of the search vector */
- get_f_norm_max(cr,&(inputrec->opts),mdatoms,p,&pnorm,NULL,NULL);
-
- /* Just in case stepsize reaches zero due to numerical precision... */
- if(stepsize<=0)
- stepsize = inputrec->em_stepsize/pnorm;
-
- /*
- * Double check the value of the derivative in the search direction.
- * If it is positive it must be due to the old information in the
- * CG formula, so just remove that and start over with beta=0.
- * This corresponds to a steepest descent step.
- */
- if(gpa>0) {
- beta = 0;
- step--; /* Don't count this step since we are restarting */
- continue; /* Go back to the beginning of the big for-loop */
- }
-
- /* Calculate minimum allowed stepsize, before the average (norm)
- * relative change in coordinate is smaller than precision
- */
- minstep=0;
- for (i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
- for(m=0; m<DIM; m++) {
- tmp = fabs(s_min->s.x[i][m]);
- if(tmp < 1.0)
- tmp = 1.0;
- tmp = p[i][m]/tmp;
- minstep += tmp*tmp;
- }
- }
- /* Add up from all CPUs */
- if(PAR(cr))
- gmx_sumd(1,&minstep,cr);
-
- minstep = GMX_REAL_EPS/sqrt(minstep/(3*state_global->natoms));
-
- if(stepsize<minstep) {
- converged=TRUE;
- break;
- }
-
- /* Write coordinates if necessary */
- do_x = do_per_step(step,inputrec->nstxout);
- do_f = do_per_step(step,inputrec->nstfout);
-
- write_em_traj(fplog,cr,outf,do_x,do_f,NULL,
- top_global,inputrec,step,
- s_min,state_global,f_global);
-
- /* Take a step downhill.
- * In theory, we should minimize the function along this direction.
- * That is quite possible, but it turns out to take 5-10 function evaluations
- * for each line. However, we dont really need to find the exact minimum -
- * it is much better to start a new CG step in a modified direction as soon
- * as we are close to it. This will save a lot of energy evaluations.
- *
- * In practice, we just try to take a single step.
- * If it worked (i.e. lowered the energy), we increase the stepsize but
- * the continue straight to the next CG step without trying to find any minimum.
- * If it didn't work (higher energy), there must be a minimum somewhere between
- * the old position and the new one.
- *
- * Due to the finite numerical accuracy, it turns out that it is a good idea
- * to even accept a SMALL increase in energy, if the derivative is still downhill.
- * This leads to lower final energies in the tests I've done. / Erik
- */
- s_a->epot = s_min->epot;
- a = 0.0;
- c = a + stepsize; /* reference position along line is zero */
-
- if (DOMAINDECOMP(cr) && s_min->s.ddp_count < cr->dd->ddp_count) {
- em_dd_partition_system(fplog,step,cr,top_global,inputrec,
- s_min,top,mdatoms,fr,vsite,constr,
- nrnb,wcycle);
- }
-
- /* Take a trial step (new coords in s_c) */
- do_em_step(cr,inputrec,mdatoms,s_min,c,s_min->s.cg_p,s_c,
- constr,top,nrnb,wcycle,-1);
-
- neval++;
- /* Calculate energy for the trial step */
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,s_c,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,-1,FALSE);
-
- /* Calc derivative along line */
- p = s_c->s.cg_p;
- sf = s_c->f;
- gpc=0;
- for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
- for(m=0; m<DIM; m++)
- gpc -= p[i][m]*sf[i][m]; /* f is negative gradient, thus the sign */
- }
- /* Sum the gradient along the line across CPUs */
- if (PAR(cr))
- gmx_sumd(1,&gpc,cr);
-
- /* This is the max amount of increase in energy we tolerate */
- tmp=sqrt(GMX_REAL_EPS)*fabs(s_a->epot);
-
- /* Accept the step if the energy is lower, or if it is not significantly higher
- * and the line derivative is still negative.
- */
- if (s_c->epot < s_a->epot || (gpc < 0 && s_c->epot < (s_a->epot + tmp))) {
- foundlower = TRUE;
- /* Great, we found a better energy. Increase step for next iteration
- * if we are still going down, decrease it otherwise
- */
- if(gpc<0)
- stepsize *= 1.618034; /* The golden section */
- else
- stepsize *= 0.618034; /* 1/golden section */
- } else {
- /* New energy is the same or higher. We will have to do some work
- * to find a smaller value in the interval. Take smaller step next time!
- */
- foundlower = FALSE;
- stepsize *= 0.618034;
- }
-
-
-
-
- /* OK, if we didn't find a lower value we will have to locate one now - there must
- * be one in the interval [a=0,c].
- * The same thing is valid here, though: Don't spend dozens of iterations to find
- * the line minimum. We try to interpolate based on the derivative at the endpoints,
- * and only continue until we find a lower value. In most cases this means 1-2 iterations.
- *
- * I also have a safeguard for potentially really patological functions so we never
- * take more than 20 steps before we give up ...
- *
- * If we already found a lower value we just skip this step and continue to the update.
- */
- if (!foundlower) {
- nminstep=0;
-
- do {
- /* Select a new trial point.
- * If the derivatives at points a & c have different sign we interpolate to zero,
- * otherwise just do a bisection.
- */
- if(gpa<0 && gpc>0)
- b = a + gpa*(a-c)/(gpc-gpa);
- else
- b = 0.5*(a+c);
-
- /* safeguard if interpolation close to machine accuracy causes errors:
- * never go outside the interval
- */
- if(b<=a || b>=c)
- b = 0.5*(a+c);
-
- if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count) {
- /* Reload the old state */
- em_dd_partition_system(fplog,-1,cr,top_global,inputrec,
- s_min,top,mdatoms,fr,vsite,constr,
- nrnb,wcycle);
- }
-
- /* Take a trial step to this new point - new coords in s_b */
- do_em_step(cr,inputrec,mdatoms,s_min,b,s_min->s.cg_p,s_b,
- constr,top,nrnb,wcycle,-1);
-
- neval++;
- /* Calculate energy for the trial step */
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,s_b,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,-1,FALSE);
-
- /* p does not change within a step, but since the domain decomposition
- * might change, we have to use cg_p of s_b here.
- */
- p = s_b->s.cg_p;
- sf = s_b->f;
- gpb=0;
- for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
- for(m=0; m<DIM; m++)
- gpb -= p[i][m]*sf[i][m]; /* f is negative gradient, thus the sign */
- }
- /* Sum the gradient along the line across CPUs */
- if (PAR(cr))
- gmx_sumd(1,&gpb,cr);
-
- if (debug)
- fprintf(debug,"CGE: EpotA %f EpotB %f EpotC %f gpb %f\n",
- s_a->epot,s_b->epot,s_c->epot,gpb);
-
- epot_repl = s_b->epot;
-
- /* Keep one of the intervals based on the value of the derivative at the new point */
- if (gpb > 0) {
- /* Replace c endpoint with b */
- swap_em_state(s_b,s_c);
- c = b;
- gpc = gpb;
- } else {
- /* Replace a endpoint with b */
- swap_em_state(s_b,s_a);
- a = b;
- gpa = gpb;
- }
-
- /*
- * Stop search as soon as we find a value smaller than the endpoints.
- * Never run more than 20 steps, no matter what.
- */
- nminstep++;
- } while ((epot_repl > s_a->epot || epot_repl > s_c->epot) &&
- (nminstep < 20));
-
- if (fabs(epot_repl - s_min->epot) < fabs(s_min->epot)*GMX_REAL_EPS ||
- nminstep >= 20) {
- /* OK. We couldn't find a significantly lower energy.
- * If beta==0 this was steepest descent, and then we give up.
- * If not, set beta=0 and restart with steepest descent before quitting.
- */
- if (beta == 0.0) {
- /* Converged */
- converged = TRUE;
- break;
- } else {
- /* Reset memory before giving up */
- beta = 0.0;
- continue;
- }
- }
-
- /* Select min energy state of A & C, put the best in B.
- */
- if (s_c->epot < s_a->epot) {
- if (debug)
- fprintf(debug,"CGE: C (%f) is lower than A (%f), moving C to B\n",
- s_c->epot,s_a->epot);
- swap_em_state(s_b,s_c);
- gpb = gpc;
- b = c;
- } else {
- if (debug)
- fprintf(debug,"CGE: A (%f) is lower than C (%f), moving A to B\n",
- s_a->epot,s_c->epot);
- swap_em_state(s_b,s_a);
- gpb = gpa;
- b = a;
- }
-
- } else {
- if (debug)
- fprintf(debug,"CGE: Found a lower energy %f, moving C to B\n",
- s_c->epot);
- swap_em_state(s_b,s_c);
- gpb = gpc;
- b = c;
- }
-
- /* new search direction */
- /* beta = 0 means forget all memory and restart with steepest descents. */
- if (nstcg && ((step % nstcg)==0))
- beta = 0.0;
- else {
- /* s_min->fnorm cannot be zero, because then we would have converged
- * and broken out.
- */
-
- /* Polak-Ribiere update.
- * Change to fnorm2/fnorm2_old for Fletcher-Reeves
- */
- beta = pr_beta(cr,&inputrec->opts,mdatoms,top_global,s_min,s_b);
- }
- /* Limit beta to prevent oscillations */
- if (fabs(beta) > 5.0)
- beta = 0.0;
-
-
- /* update positions */
- swap_em_state(s_min,s_b);
- gpa = gpb;
-
- /* Print it if necessary */
- if (MASTER(cr)) {
- if(bVerbose)
- fprintf(stderr,"\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
- step,s_min->epot,s_min->fnorm/sqrt(state_global->natoms),
- s_min->fmax,s_min->a_fmax+1);
- /* Store the new (lower) energies */
- upd_mdebin(mdebin,FALSE,FALSE,(double)step,
- mdatoms->tmass,enerd,&s_min->s,s_min->s.box,
- NULL,NULL,vir,pres,NULL,mu_tot,constr);
- do_log = do_per_step(step,inputrec->nstlog);
- do_ene = do_per_step(step,inputrec->nstenergy);
- if(do_log)
- print_ebin_header(fplog,step,step,s_min->s.lambda);
- print_ebin(outf->fp_ene,do_ene,FALSE,FALSE,
- do_log ? fplog : NULL,step,step,eprNORMAL,
- TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
- }
-
- /* Stop when the maximum force lies below tolerance.
- * If we have reached machine precision, converged is already set to true.
- */
- converged = converged || (s_min->fmax < inputrec->em_tol);
-
- } /* End of the loop */
-
- if (converged)
- step--; /* we never took that last step in this case */
-
- if (s_min->fmax > inputrec->em_tol)
- {
- if (MASTER(cr))
- {
- warn_step(stderr,inputrec->em_tol,step-1==number_steps,FALSE);
- warn_step(fplog ,inputrec->em_tol,step-1==number_steps,FALSE);
- }
- converged = FALSE;
- }
-
- if (MASTER(cr)) {
- /* If we printed energy and/or logfile last step (which was the last step)
- * we don't have to do it again, but otherwise print the final values.
- */
- if(!do_log) {
- /* Write final value to log since we didn't do anything the last step */
- print_ebin_header(fplog,step,step,s_min->s.lambda);
- }
- if (!do_ene || !do_log) {
- /* Write final energy file entries */
- print_ebin(outf->fp_ene,!do_ene,FALSE,FALSE,
- !do_log ? fplog : NULL,step,step,eprNORMAL,
- TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
- }
- }
-
- /* Print some stuff... */
- if (MASTER(cr))
- fprintf(stderr,"\nwriting lowest energy coordinates.\n");
-
- /* IMPORTANT!
- * For accurate normal mode calculation it is imperative that we
- * store the last conformation into the full precision binary trajectory.
- *
- * However, we should only do it if we did NOT already write this step
- * above (which we did if do_x or do_f was true).
- */
- do_x = !do_per_step(step,inputrec->nstxout);
- do_f = (inputrec->nstfout > 0 && !do_per_step(step,inputrec->nstfout));
-
- write_em_traj(fplog,cr,outf,do_x,do_f,ftp2fn(efSTO,nfile,fnm),
- top_global,inputrec,step,
- s_min,state_global,f_global);
-
- fnormn = s_min->fnorm/sqrt(state_global->natoms);
-
- if (MASTER(cr)) {
- print_converged(stderr,CG,inputrec->em_tol,step,converged,number_steps,
- s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
- print_converged(fplog,CG,inputrec->em_tol,step,converged,number_steps,
- s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
-
- fprintf(fplog,"\nPerformed %d energy evaluations in total.\n",neval);
- }
-
- finish_em(fplog,cr,outf,runtime,wcycle);
-
- /* To print the actual number of steps we needed somewhere */
- runtime->nsteps_done = step;
-
- return 0;
-} /* That's all folks */
-
-
-double do_lbfgs(FILE *fplog,t_commrec *cr,
- int nfile,const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,
- t_inputrec *inputrec,
- gmx_mtop_t *top_global,t_fcdata *fcd,
- t_state *state,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,
- t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime)
-{
- static const char *LBFGS="Low-Memory BFGS Minimizer";
- em_state_t ems;
- gmx_localtop_t *top;
- gmx_enerdata_t *enerd;
- rvec *f;
- gmx_global_stat_t gstat;
- t_graph *graph;
- rvec *f_global;
- int ncorr,nmaxcorr,point,cp,neval,nminstep;
- double stepsize,gpa,gpb,gpc,tmp,minstep;
- real *rho,*alpha,*ff,*xx,*p,*s,*lastx,*lastf,**dx,**dg;
- real *xa,*xb,*xc,*fa,*fb,*fc,*xtmp,*ftmp;
- real a,b,c,maxdelta,delta;
- real diag,Epot0,Epot,EpotA,EpotB,EpotC;
- real dgdx,dgdg,sq,yr,beta;
- t_mdebin *mdebin;
- gmx_bool converged,first;
- rvec mu_tot;
- real fnorm,fmax;
- gmx_bool do_log,do_ene,do_x,do_f,foundlower,*frozen;
- tensor vir,pres;
- int start,end,number_steps;
- gmx_mdoutf_t *outf;
- int i,k,m,n,nfmax,gf,step;
- /* not used */
- real terminate;
-
- if (PAR(cr))
- gmx_fatal(FARGS,"Cannot do parallel L-BFGS Minimization - yet.\n");
-
- n = 3*state->natoms;
- nmaxcorr = inputrec->nbfgscorr;
-
- /* Allocate memory */
- /* Use pointers to real so we dont have to loop over both atoms and
- * dimensions all the time...
- * x/f are allocated as rvec *, so make new x0/f0 pointers-to-real
- * that point to the same memory.
- */
- snew(xa,n);
- snew(xb,n);
- snew(xc,n);
- snew(fa,n);
- snew(fb,n);
- snew(fc,n);
- snew(frozen,n);
-
- snew(p,n);
- snew(lastx,n);
- snew(lastf,n);
- snew(rho,nmaxcorr);
- snew(alpha,nmaxcorr);
-
- snew(dx,nmaxcorr);
- for(i=0;i<nmaxcorr;i++)
- snew(dx[i],n);
-
- snew(dg,nmaxcorr);
- for(i=0;i<nmaxcorr;i++)
- snew(dg[i],n);
-
- step = 0;
- neval = 0;
-
- /* Init em */
- init_em(fplog,LBFGS,cr,inputrec,
- state,top_global,&ems,&top,&f,&f_global,
- nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
- nfile,fnm,&outf,&mdebin);
- /* Do_lbfgs is not completely updated like do_steep and do_cg,
- * so we free some memory again.
- */
- sfree(ems.s.x);
- sfree(ems.f);
-
- xx = (real *)state->x;
- ff = (real *)f;
-
- start = mdatoms->start;
- end = mdatoms->homenr + start;
-
- /* Print to log file */
- print_em_start(fplog,cr,runtime,wcycle,LBFGS);
-
- do_log = do_ene = do_x = do_f = TRUE;
-
- /* Max number of steps */
- number_steps=inputrec->nsteps;
-
- /* Create a 3*natoms index to tell whether each degree of freedom is frozen */
- gf = 0;
- for(i=start; i<end; i++) {
- if (mdatoms->cFREEZE)
- gf = mdatoms->cFREEZE[i];
- for(m=0; m<DIM; m++)
- frozen[3*i+m]=inputrec->opts.nFreeze[gf][m];
- }
- if (MASTER(cr))
- sp_header(stderr,LBFGS,inputrec->em_tol,number_steps);
- if (fplog)
- sp_header(fplog,LBFGS,inputrec->em_tol,number_steps);
-
- if (vsite)
- construct_vsites(fplog,vsite,state->x,nrnb,1,NULL,
- top->idef.iparams,top->idef.il,
- fr->ePBC,fr->bMolPBC,graph,cr,state->box);
-
- /* Call the force routine and some auxiliary (neighboursearching etc.) */
- /* do_force always puts the charge groups in the box and shifts again
- * We do not unshift, so molecules are always whole
- */
- neval++;
- ems.s.x = state->x;
- ems.f = f;
- evaluate_energy(fplog,bVerbose,cr,
- state,top_global,&ems,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,-1,TRUE);
- where();
-
- if (MASTER(cr)) {
- /* Copy stuff to the energy bin for easy printing etc. */
- upd_mdebin(mdebin,FALSE,FALSE,(double)step,
- mdatoms->tmass,enerd,state,state->box,
- NULL,NULL,vir,pres,NULL,mu_tot,constr);
-
- print_ebin_header(fplog,step,step,state->lambda);
- print_ebin(outf->fp_ene,TRUE,FALSE,FALSE,fplog,step,step,eprNORMAL,
- TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
- }
- where();
-
- /* This is the starting energy */
- Epot = enerd->term[F_EPOT];
-
- fnorm = ems.fnorm;
- fmax = ems.fmax;
- nfmax = ems.a_fmax;
-
- /* Set the initial step.
- * since it will be multiplied by the non-normalized search direction
- * vector (force vector the first time), we scale it by the
- * norm of the force.
- */
-
- if (MASTER(cr)) {
- fprintf(stderr,"Using %d BFGS correction steps.\n\n",nmaxcorr);
- fprintf(stderr," F-max = %12.5e on atom %d\n",fmax,nfmax+1);
- fprintf(stderr," F-Norm = %12.5e\n",fnorm/sqrt(state->natoms));
- fprintf(stderr,"\n");
- /* and copy to the log file too... */
- fprintf(fplog,"Using %d BFGS correction steps.\n\n",nmaxcorr);
- fprintf(fplog," F-max = %12.5e on atom %d\n",fmax,nfmax+1);
- fprintf(fplog," F-Norm = %12.5e\n",fnorm/sqrt(state->natoms));
- fprintf(fplog,"\n");
- }
-
- point=0;
- for(i=0;i<n;i++)
- if(!frozen[i])
- dx[point][i] = ff[i]; /* Initial search direction */
- else
- dx[point][i] = 0;
-
- stepsize = 1.0/fnorm;
- converged = FALSE;
-
- /* Start the loop over BFGS steps.
- * Each successful step is counted, and we continue until
- * we either converge or reach the max number of steps.
- */
-
- ncorr=0;
-
- /* Set the gradient from the force */
- converged = FALSE;
- for(step=0; (number_steps<0 || (number_steps>=0 && step<=number_steps)) && !converged; step++) {
-
- /* Write coordinates if necessary */
- do_x = do_per_step(step,inputrec->nstxout);
- do_f = do_per_step(step,inputrec->nstfout);
-
- write_traj(fplog,cr,outf,MDOF_X | MDOF_F,
- top_global,step,(real)step,state,state,f,f,NULL,NULL);
-
- /* Do the linesearching in the direction dx[point][0..(n-1)] */
-
- /* pointer to current direction - point=0 first time here */
- s=dx[point];
-
- /* calculate line gradient */
- for(gpa=0,i=0;i<n;i++)
- gpa-=s[i]*ff[i];
-
- /* Calculate minimum allowed stepsize, before the average (norm)
- * relative change in coordinate is smaller than precision
- */
- for(minstep=0,i=0;i<n;i++) {
- tmp=fabs(xx[i]);
- if(tmp<1.0)
- tmp=1.0;
- tmp = s[i]/tmp;
- minstep += tmp*tmp;
- }
- minstep = GMX_REAL_EPS/sqrt(minstep/n);
-
- if(stepsize<minstep) {
- converged=TRUE;
- break;
- }
-
- /* Store old forces and coordinates */
- for(i=0;i<n;i++) {
- lastx[i]=xx[i];
- lastf[i]=ff[i];
- }
- Epot0=Epot;
-
- first=TRUE;
-
- for(i=0;i<n;i++)
- xa[i]=xx[i];
-
- /* Take a step downhill.
- * In theory, we should minimize the function along this direction.
- * That is quite possible, but it turns out to take 5-10 function evaluations
- * for each line. However, we dont really need to find the exact minimum -
- * it is much better to start a new BFGS step in a modified direction as soon
- * as we are close to it. This will save a lot of energy evaluations.
- *
- * In practice, we just try to take a single step.
- * If it worked (i.e. lowered the energy), we increase the stepsize but
- * the continue straight to the next BFGS step without trying to find any minimum.
- * If it didn't work (higher energy), there must be a minimum somewhere between
- * the old position and the new one.
- *
- * Due to the finite numerical accuracy, it turns out that it is a good idea
- * to even accept a SMALL increase in energy, if the derivative is still downhill.
- * This leads to lower final energies in the tests I've done. / Erik
- */
- foundlower=FALSE;
- EpotA = Epot0;
- a = 0.0;
- c = a + stepsize; /* reference position along line is zero */
-
- /* Check stepsize first. We do not allow displacements
- * larger than emstep.
- */
- do {
- c = a + stepsize;
- maxdelta=0;
- for(i=0;i<n;i++) {
- delta=c*s[i];
- if(delta>maxdelta)
- maxdelta=delta;
- }
- if(maxdelta>inputrec->em_stepsize)
- stepsize*=0.1;
- } while(maxdelta>inputrec->em_stepsize);
-
- /* Take a trial step */
- for (i=0; i<n; i++)
- xc[i] = lastx[i] + c*s[i];
-
- neval++;
- /* Calculate energy for the trial step */
- ems.s.x = (rvec *)xc;
- ems.f = (rvec *)fc;
- evaluate_energy(fplog,bVerbose,cr,
- state,top_global,&ems,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,step,FALSE);
- EpotC = ems.epot;
-
- /* Calc derivative along line */
- for(gpc=0,i=0; i<n; i++) {
- gpc -= s[i]*fc[i]; /* f is negative gradient, thus the sign */
- }
- /* Sum the gradient along the line across CPUs */
- if (PAR(cr))
- gmx_sumd(1,&gpc,cr);
-
- /* This is the max amount of increase in energy we tolerate */
- tmp=sqrt(GMX_REAL_EPS)*fabs(EpotA);
-
- /* Accept the step if the energy is lower, or if it is not significantly higher
- * and the line derivative is still negative.
- */
- if(EpotC<EpotA || (gpc<0 && EpotC<(EpotA+tmp))) {
- foundlower = TRUE;
- /* Great, we found a better energy. Increase step for next iteration
- * if we are still going down, decrease it otherwise
- */
- if(gpc<0)
- stepsize *= 1.618034; /* The golden section */
- else
- stepsize *= 0.618034; /* 1/golden section */
- } else {
- /* New energy is the same or higher. We will have to do some work
- * to find a smaller value in the interval. Take smaller step next time!
- */
- foundlower = FALSE;
- stepsize *= 0.618034;
- }
-
- /* OK, if we didn't find a lower value we will have to locate one now - there must
- * be one in the interval [a=0,c].
- * The same thing is valid here, though: Don't spend dozens of iterations to find
- * the line minimum. We try to interpolate based on the derivative at the endpoints,
- * and only continue until we find a lower value. In most cases this means 1-2 iterations.
- *
- * I also have a safeguard for potentially really patological functions so we never
- * take more than 20 steps before we give up ...
- *
- * If we already found a lower value we just skip this step and continue to the update.
- */
-
- if(!foundlower) {
-
- nminstep=0;
- do {
- /* Select a new trial point.
- * If the derivatives at points a & c have different sign we interpolate to zero,
- * otherwise just do a bisection.
- */
-
- if(gpa<0 && gpc>0)
- b = a + gpa*(a-c)/(gpc-gpa);
- else
- b = 0.5*(a+c);
-
- /* safeguard if interpolation close to machine accuracy causes errors:
- * never go outside the interval
- */
- if(b<=a || b>=c)
- b = 0.5*(a+c);
-
- /* Take a trial step */
- for (i=0; i<n; i++)
- xb[i] = lastx[i] + b*s[i];
-
- neval++;
- /* Calculate energy for the trial step */
- ems.s.x = (rvec *)xb;
- ems.f = (rvec *)fb;
- evaluate_energy(fplog,bVerbose,cr,
- state,top_global,&ems,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,step,FALSE);
- EpotB = ems.epot;
-
- fnorm = ems.fnorm;
-
- for(gpb=0,i=0; i<n; i++)
- gpb -= s[i]*fb[i]; /* f is negative gradient, thus the sign */
-
- /* Sum the gradient along the line across CPUs */
- if (PAR(cr))
- gmx_sumd(1,&gpb,cr);
-
- /* Keep one of the intervals based on the value of the derivative at the new point */
- if(gpb>0) {
- /* Replace c endpoint with b */
- EpotC = EpotB;
- c = b;
- gpc = gpb;
- /* swap coord pointers b/c */
- xtmp = xb;
- ftmp = fb;
- xb = xc;
- fb = fc;
- xc = xtmp;
- fc = ftmp;
- } else {
- /* Replace a endpoint with b */
- EpotA = EpotB;
- a = b;
- gpa = gpb;
- /* swap coord pointers a/b */
- xtmp = xb;
- ftmp = fb;
- xb = xa;
- fb = fa;
- xa = xtmp;
- fa = ftmp;
- }
-
- /*
- * Stop search as soon as we find a value smaller than the endpoints,
- * or if the tolerance is below machine precision.
- * Never run more than 20 steps, no matter what.
- */
- nminstep++;
- } while((EpotB>EpotA || EpotB>EpotC) && (nminstep<20));
-
- if(fabs(EpotB-Epot0)<GMX_REAL_EPS || nminstep>=20) {
- /* OK. We couldn't find a significantly lower energy.
- * If ncorr==0 this was steepest descent, and then we give up.
- * If not, reset memory to restart as steepest descent before quitting.
- */
- if(ncorr==0) {
- /* Converged */
- converged=TRUE;
- break;
- } else {
- /* Reset memory */
- ncorr=0;
- /* Search in gradient direction */
- for(i=0;i<n;i++)
- dx[point][i]=ff[i];
- /* Reset stepsize */
- stepsize = 1.0/fnorm;
- continue;
- }
- }
-
- /* Select min energy state of A & C, put the best in xx/ff/Epot
- */
- if(EpotC<EpotA) {
- Epot = EpotC;
- /* Use state C */
- for(i=0;i<n;i++) {
- xx[i]=xc[i];
- ff[i]=fc[i];
- }
- stepsize=c;
- } else {
- Epot = EpotA;
- /* Use state A */
- for(i=0;i<n;i++) {
- xx[i]=xa[i];
- ff[i]=fa[i];
- }
- stepsize=a;
- }
-
- } else {
- /* found lower */
- Epot = EpotC;
- /* Use state C */
- for(i=0;i<n;i++) {
- xx[i]=xc[i];
- ff[i]=fc[i];
- }
- stepsize=c;
- }
-
- /* Update the memory information, and calculate a new
- * approximation of the inverse hessian
- */
-
- /* Have new data in Epot, xx, ff */
- if(ncorr<nmaxcorr)
- ncorr++;
-
- for(i=0;i<n;i++) {
- dg[point][i]=lastf[i]-ff[i];
- dx[point][i]*=stepsize;
- }
-
- dgdg=0;
- dgdx=0;
- for(i=0;i<n;i++) {
- dgdg+=dg[point][i]*dg[point][i];
- dgdx+=dg[point][i]*dx[point][i];
- }
-
- diag=dgdx/dgdg;
-
- rho[point]=1.0/dgdx;
- point++;
-
- if(point>=nmaxcorr)
- point=0;
-
- /* Update */
- for(i=0;i<n;i++)
- p[i]=ff[i];
-
- cp=point;
-
- /* Recursive update. First go back over the memory points */
- for(k=0;k<ncorr;k++) {
- cp--;
- if(cp<0)
- cp=ncorr-1;
-
- sq=0;
- for(i=0;i<n;i++)
- sq+=dx[cp][i]*p[i];
-
- alpha[cp]=rho[cp]*sq;
-
- for(i=0;i<n;i++)
- p[i] -= alpha[cp]*dg[cp][i];
- }
-
- for(i=0;i<n;i++)
- p[i] *= diag;
-
- /* And then go forward again */
- for(k=0;k<ncorr;k++) {
- yr = 0;
- for(i=0;i<n;i++)
- yr += p[i]*dg[cp][i];
-
- beta = rho[cp]*yr;
- beta = alpha[cp]-beta;
-
- for(i=0;i<n;i++)
- p[i] += beta*dx[cp][i];
-
- cp++;
- if(cp>=ncorr)
- cp=0;
- }
-
- for(i=0;i<n;i++)
- if(!frozen[i])
- dx[point][i] = p[i];
- else
- dx[point][i] = 0;
-
- stepsize=1.0;
-
- /* Test whether the convergence criterion is met */
- get_f_norm_max(cr,&(inputrec->opts),mdatoms,f,&fnorm,&fmax,&nfmax);
-
- /* Print it if necessary */
- if (MASTER(cr)) {
- if(bVerbose)
- fprintf(stderr,"\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
- step,Epot,fnorm/sqrt(state->natoms),fmax,nfmax+1);
- /* Store the new (lower) energies */
- upd_mdebin(mdebin,FALSE,FALSE,(double)step,
- mdatoms->tmass,enerd,state,state->box,
- NULL,NULL,vir,pres,NULL,mu_tot,constr);
- do_log = do_per_step(step,inputrec->nstlog);
- do_ene = do_per_step(step,inputrec->nstenergy);
- if(do_log)
- print_ebin_header(fplog,step,step,state->lambda);
- print_ebin(outf->fp_ene,do_ene,FALSE,FALSE,
- do_log ? fplog : NULL,step,step,eprNORMAL,
- TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
- }
-
- /* Stop when the maximum force lies below tolerance.
- * If we have reached machine precision, converged is already set to true.
- */
-
- converged = converged || (fmax < inputrec->em_tol);
-
- } /* End of the loop */
-
- if(converged)
- step--; /* we never took that last step in this case */
-
- if(fmax>inputrec->em_tol)
- {
- if (MASTER(cr))
- {
- warn_step(stderr,inputrec->em_tol,step-1==number_steps,FALSE);
- warn_step(fplog ,inputrec->em_tol,step-1==number_steps,FALSE);
- }
- converged = FALSE;
- }
-
- /* If we printed energy and/or logfile last step (which was the last step)
- * we don't have to do it again, but otherwise print the final values.
- */
- if(!do_log) /* Write final value to log since we didn't do anythin last step */
- print_ebin_header(fplog,step,step,state->lambda);
- if(!do_ene || !do_log) /* Write final energy file entries */
- print_ebin(outf->fp_ene,!do_ene,FALSE,FALSE,
- !do_log ? fplog : NULL,step,step,eprNORMAL,
- TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
-
- /* Print some stuff... */
- if (MASTER(cr))
- fprintf(stderr,"\nwriting lowest energy coordinates.\n");
-
- /* IMPORTANT!
- * For accurate normal mode calculation it is imperative that we
- * store the last conformation into the full precision binary trajectory.
- *
- * However, we should only do it if we did NOT already write this step
- * above (which we did if do_x or do_f was true).
- */
- do_x = !do_per_step(step,inputrec->nstxout);
- do_f = !do_per_step(step,inputrec->nstfout);
- write_em_traj(fplog,cr,outf,do_x,do_f,ftp2fn(efSTO,nfile,fnm),
- top_global,inputrec,step,
- &ems,state,f);
-
- if (MASTER(cr)) {
- print_converged(stderr,LBFGS,inputrec->em_tol,step,converged,
- number_steps,Epot,fmax,nfmax,fnorm/sqrt(state->natoms));
- print_converged(fplog,LBFGS,inputrec->em_tol,step,converged,
- number_steps,Epot,fmax,nfmax,fnorm/sqrt(state->natoms));
-
- fprintf(fplog,"\nPerformed %d energy evaluations in total.\n",neval);
- }
-
- finish_em(fplog,cr,outf,runtime,wcycle);
-
- /* To print the actual number of steps we needed somewhere */
- runtime->nsteps_done = step;
-
- return 0;
-} /* That's all folks */
-
-
-double do_steep(FILE *fplog,t_commrec *cr,
- int nfile, const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,
- t_inputrec *inputrec,
- gmx_mtop_t *top_global,t_fcdata *fcd,
- t_state *state_global,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,
- t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime)
-{
- const char *SD="Steepest Descents";
- em_state_t *s_min,*s_try;
- rvec *f_global;
- gmx_localtop_t *top;
- gmx_enerdata_t *enerd;
- rvec *f;
- gmx_global_stat_t gstat;
- t_graph *graph;
- real stepsize,constepsize;
- real ustep,dvdlambda,fnormn;
- gmx_mdoutf_t *outf;
- t_mdebin *mdebin;
- gmx_bool bDone,bAbort,do_x,do_f;
- tensor vir,pres;
- rvec mu_tot;
- int nsteps;
- int count=0;
- int steps_accepted=0;
- /* not used */
- real terminate=0;
-
- s_min = init_em_state();
- s_try = init_em_state();
-
- /* Init em and store the local state in s_try */
- init_em(fplog,SD,cr,inputrec,
- state_global,top_global,s_try,&top,&f,&f_global,
- nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
- nfile,fnm,&outf,&mdebin);
-
- /* Print to log file */
- print_em_start(fplog,cr,runtime,wcycle,SD);
-
- /* Set variables for stepsize (in nm). This is the largest
- * step that we are going to make in any direction.
- */
- ustep = inputrec->em_stepsize;
- stepsize = 0;
-
- /* Max number of steps */
- nsteps = inputrec->nsteps;
-
- if (MASTER(cr))
- /* Print to the screen */
- sp_header(stderr,SD,inputrec->em_tol,nsteps);
- if (fplog)
- sp_header(fplog,SD,inputrec->em_tol,nsteps);
-
- /**** HERE STARTS THE LOOP ****
- * count is the counter for the number of steps
- * bDone will be TRUE when the minimization has converged
- * bAbort will be TRUE when nsteps steps have been performed or when
- * the stepsize becomes smaller than is reasonable for machine precision
- */
- count = 0;
- bDone = FALSE;
- bAbort = FALSE;
- while( !bDone && !bAbort ) {
- bAbort = (nsteps >= 0) && (count == nsteps);
-
- /* set new coordinates, except for first step */
- if (count > 0) {
- do_em_step(cr,inputrec,mdatoms,s_min,stepsize,s_min->f,s_try,
- constr,top,nrnb,wcycle,count);
- }
-
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,s_try,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,count,count==0);
-
- if (MASTER(cr))
- print_ebin_header(fplog,count,count,s_try->s.lambda);
-
- if (count == 0)
- s_min->epot = s_try->epot + 1;
-
- /* Print it if necessary */
- if (MASTER(cr)) {
- if (bVerbose) {
- fprintf(stderr,"Step=%5d, Dmax= %6.1e nm, Epot= %12.5e Fmax= %11.5e, atom= %d%c",
- count,ustep,s_try->epot,s_try->fmax,s_try->a_fmax+1,
- (s_try->epot < s_min->epot) ? '\n' : '\r');
- }
-
- if (s_try->epot < s_min->epot) {
- /* Store the new (lower) energies */
- upd_mdebin(mdebin,FALSE,FALSE,(double)count,
- mdatoms->tmass,enerd,&s_try->s,s_try->s.box,
- NULL,NULL,vir,pres,NULL,mu_tot,constr);
- print_ebin(outf->fp_ene,TRUE,
- do_per_step(steps_accepted,inputrec->nstdisreout),
- do_per_step(steps_accepted,inputrec->nstorireout),
- fplog,count,count,eprNORMAL,TRUE,
- mdebin,fcd,&(top_global->groups),&(inputrec->opts));
- fflush(fplog);
- }
- }
-
- /* Now if the new energy is smaller than the previous...
- * or if this is the first step!
- * or if we did random steps!
- */
-
- if ( (count==0) || (s_try->epot < s_min->epot) ) {
- steps_accepted++;
-
- /* Test whether the convergence criterion is met... */
- bDone = (s_try->fmax < inputrec->em_tol);
-
- /* Copy the arrays for force, positions and energy */
- /* The 'Min' array always holds the coords and forces of the minimal
- sampled energy */
- swap_em_state(s_min,s_try);
- if (count > 0)
- ustep *= 1.2;
-
- /* Write to trn, if necessary */
- do_x = do_per_step(steps_accepted,inputrec->nstxout);
- do_f = do_per_step(steps_accepted,inputrec->nstfout);
- write_em_traj(fplog,cr,outf,do_x,do_f,NULL,
- top_global,inputrec,count,
- s_min,state_global,f_global);
- }
- else {
- /* If energy is not smaller make the step smaller... */
- ustep *= 0.5;
-
- if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count) {
- /* Reload the old state */
- em_dd_partition_system(fplog,count,cr,top_global,inputrec,
- s_min,top,mdatoms,fr,vsite,constr,
- nrnb,wcycle);
- }
- }
-
- /* Determine new step */
- stepsize = ustep/s_min->fmax;
-
- /* Check if stepsize is too small, with 1 nm as a characteristic length */
-#ifdef GMX_DOUBLE
- if (count == nsteps || ustep < 1e-12)
-#else
- if (count == nsteps || ustep < 1e-6)
-#endif
- {
- if (MASTER(cr))
- {
- warn_step(stderr,inputrec->em_tol,count==nsteps,constr!=NULL);
- warn_step(fplog ,inputrec->em_tol,count==nsteps,constr!=NULL);
- }
- bAbort=TRUE;
- }
-
- count++;
- } /* End of the loop */
-
- /* Print some shit... */
- if (MASTER(cr))
- fprintf(stderr,"\nwriting lowest energy coordinates.\n");
- write_em_traj(fplog,cr,outf,TRUE,inputrec->nstfout,ftp2fn(efSTO,nfile,fnm),
- top_global,inputrec,count,
- s_min,state_global,f_global);
-
- fnormn = s_min->fnorm/sqrt(state_global->natoms);
-
- if (MASTER(cr)) {
- print_converged(stderr,SD,inputrec->em_tol,count,bDone,nsteps,
- s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
- print_converged(fplog,SD,inputrec->em_tol,count,bDone,nsteps,
- s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
- }
-
- finish_em(fplog,cr,outf,runtime,wcycle);
-
- /* To print the actual number of steps we needed somewhere */
- inputrec->nsteps=count;
-
- runtime->nsteps_done = count;
-
- return 0;
-} /* That's all folks */
-
-
-double do_nm(FILE *fplog,t_commrec *cr,
- int nfile,const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,
- t_inputrec *inputrec,
- gmx_mtop_t *top_global,t_fcdata *fcd,
- t_state *state_global,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,
- t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime)
-{
- const char *NM = "Normal Mode Analysis";
- gmx_mdoutf_t *outf;
- int natoms,atom,d;
- int nnodes,node;
- rvec *f_global;
- gmx_localtop_t *top;
- gmx_enerdata_t *enerd;
- rvec *f;
- gmx_global_stat_t gstat;
- t_graph *graph;
- real t,lambda;
- gmx_bool bNS;
- tensor vir,pres;
- rvec mu_tot;
- rvec *fneg,*dfdx;
- gmx_bool bSparse; /* use sparse matrix storage format */
- size_t sz;
- gmx_sparsematrix_t * sparse_matrix = NULL;
- real * full_matrix = NULL;
- em_state_t * state_work;
-
- /* added with respect to mdrun */
- int i,j,k,row,col;
- real der_range=10.0*sqrt(GMX_REAL_EPS);
- real x_min;
- real fnorm,fmax;
-
- if (constr != NULL)
- {
- gmx_fatal(FARGS,"Constraints present with Normal Mode Analysis, this combination is not supported");
- }
-
- state_work = init_em_state();
-
- /* Init em and store the local state in state_minimum */
- init_em(fplog,NM,cr,inputrec,
- state_global,top_global,state_work,&top,
- &f,&f_global,
- nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
- nfile,fnm,&outf,NULL);
-
- natoms = top_global->natoms;
- snew(fneg,natoms);
- snew(dfdx,natoms);
-
-#ifndef GMX_DOUBLE
- if (MASTER(cr))
- {
- fprintf(stderr,
- "NOTE: This version of Gromacs has been compiled in single precision,\n"
- " which MIGHT not be accurate enough for normal mode analysis.\n"
- " Gromacs now uses sparse matrix storage, so the memory requirements\n"
- " are fairly modest even if you recompile in double precision.\n\n");
- }
-#endif
-
- /* Check if we can/should use sparse storage format.
- *
- * Sparse format is only useful when the Hessian itself is sparse, which it
- * will be when we use a cutoff.
- * For small systems (n<1000) it is easier to always use full matrix format, though.
- */
- if(EEL_FULL(fr->eeltype) || fr->rlist==0.0)
- {
- fprintf(stderr,"Non-cutoff electrostatics used, forcing full Hessian format.\n");
- bSparse = FALSE;
- }
- else if(top_global->natoms < 1000)
- {
- fprintf(stderr,"Small system size (N=%d), using full Hessian format.\n",top_global->natoms);
- bSparse = FALSE;
- }
- else
- {
- fprintf(stderr,"Using compressed symmetric sparse Hessian format.\n");
- bSparse = TRUE;
- }
-
- sz = DIM*top_global->natoms;
-
- fprintf(stderr,"Allocating Hessian memory...\n\n");
-
- if(bSparse)
- {
- sparse_matrix=gmx_sparsematrix_init(sz);
- sparse_matrix->compressed_symmetric = TRUE;
- }
- else
- {
- snew(full_matrix,sz*sz);
- }
-
- /* Initial values */
- t = inputrec->init_t;
- lambda = inputrec->init_lambda;
-
- init_nrnb(nrnb);
-
- where();
-
- /* Write start time and temperature */
- print_em_start(fplog,cr,runtime,wcycle,NM);
-
- /* fudge nr of steps to nr of atoms */
- inputrec->nsteps = natoms*2;
-
- if (MASTER(cr))
- {
- fprintf(stderr,"starting normal mode calculation '%s'\n%d steps.\n\n",
- *(top_global->name),(int)inputrec->nsteps);
- }
-
- nnodes = cr->nnodes;
-
- /* Make evaluate_energy do a single node force calculation */
- cr->nnodes = 1;
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,state_work,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,-1,TRUE);
- cr->nnodes = nnodes;
-
- /* if forces are not small, warn user */
- get_state_f_norm_max(cr,&(inputrec->opts),mdatoms,state_work);
-
- if (MASTER(cr))
- {
- fprintf(stderr,"Maximum force:%12.5e\n",state_work->fmax);
- if (state_work->fmax > 1.0e-3)
- {
- fprintf(stderr,"Maximum force probably not small enough to");
- fprintf(stderr," ensure that you are in an \nenergy well. ");
- fprintf(stderr,"Be aware that negative eigenvalues may occur");
- fprintf(stderr," when the\nresulting matrix is diagonalized.\n");
- }
- }
-
- /***********************************************************
- *
- * Loop over all pairs in matrix
- *
- * do_force called twice. Once with positive and
- * once with negative displacement
- *
- ************************************************************/
-
- /* Steps are divided one by one over the nodes */
- for(atom=cr->nodeid; atom<natoms; atom+=nnodes)
- {
-
- for (d=0; d<DIM; d++)
- {
- x_min = state_work->s.x[atom][d];
-
- state_work->s.x[atom][d] = x_min - der_range;
-
- /* Make evaluate_energy do a single node force calculation */
- cr->nnodes = 1;
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,state_work,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,atom*2,FALSE);
-
- for(i=0; i<natoms; i++)
- {
- copy_rvec(state_work->f[i], fneg[i]);
- }
-
- state_work->s.x[atom][d] = x_min + der_range;
-
- evaluate_energy(fplog,bVerbose,cr,
- state_global,top_global,state_work,top,
- inputrec,nrnb,wcycle,gstat,
- vsite,constr,fcd,graph,mdatoms,fr,
- mu_tot,enerd,vir,pres,atom*2+1,FALSE);
- cr->nnodes = nnodes;
-
- /* x is restored to original */
- state_work->s.x[atom][d] = x_min;
-
- for(j=0; j<natoms; j++)
- {
- for (k=0; (k<DIM); k++)
- {
- dfdx[j][k] =
- -(state_work->f[j][k] - fneg[j][k])/(2*der_range);
- }
- }
-
- if (!MASTER(cr))
- {
-#ifdef GMX_MPI
-#ifdef GMX_DOUBLE
-#define mpi_type MPI_DOUBLE
-#else
-#define mpi_type MPI_FLOAT
-#endif
- MPI_Send(dfdx[0],natoms*DIM,mpi_type,MASTERNODE(cr),cr->nodeid,
- cr->mpi_comm_mygroup);
-#endif
- }
- else
- {
- for(node=0; (node<nnodes && atom+node<natoms); node++)
- {
- if (node > 0)
- {
-#ifdef GMX_MPI
- MPI_Status stat;
- MPI_Recv(dfdx[0],natoms*DIM,mpi_type,node,node,
- cr->mpi_comm_mygroup,&stat);
-#undef mpi_type
-#endif
- }
-
- row = (atom + node)*DIM + d;
-
- for(j=0; j<natoms; j++)
- {
- for(k=0; k<DIM; k++)
- {
- col = j*DIM + k;
-
- if (bSparse)
- {
- if (col >= row && dfdx[j][k] != 0.0)
- {
- gmx_sparsematrix_increment_value(sparse_matrix,
- row,col,dfdx[j][k]);
- }
- }
- else
- {
- full_matrix[row*sz+col] = dfdx[j][k];
- }
- }
- }
- }
- }
-
- if (bVerbose && fplog)
- {
- fflush(fplog);
- }
- }
- /* write progress */
- if (MASTER(cr) && bVerbose)
- {
- fprintf(stderr,"\rFinished step %d out of %d",
- min(atom+nnodes,natoms),natoms);
- fflush(stderr);
- }
- }
-
- if (MASTER(cr))
- {
- fprintf(stderr,"\n\nWriting Hessian...\n");
- gmx_mtxio_write(ftp2fn(efMTX,nfile,fnm),sz,sz,full_matrix,sparse_matrix);
- }
-
- finish_em(fplog,cr,outf,runtime,wcycle);
-
- runtime->nsteps_done = natoms*2;
-
- return 0;
-}
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROwing Monsters And Cloning Shrimps
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef GMX_THREAD_SHM_FDECOMP
-#include <pthread.h>
-#endif
-
-#include <math.h>
-#include <string.h>
-#include "sysstuff.h"
-#include "smalloc.h"
-#include "macros.h"
-#include "maths.h"
-#include "vec.h"
-#include "network.h"
-#include "nsgrid.h"
-#include "force.h"
-#include "nonbonded.h"
-#include "ns.h"
-#include "pbc.h"
-#include "names.h"
-#include "gmx_fatal.h"
-#include "nrnb.h"
-#include "txtdump.h"
-#include "mtop_util.h"
-
-#include "domdec.h"
-
-
-/*
- * E X C L U S I O N H A N D L I N G
- */
-
-#ifdef DEBUG
-static void SETEXCL_(t_excl e[],atom_id i,atom_id j)
-{ e[j] = e[j] | (1<<i); }
-static void RMEXCL_(t_excl e[],atom_id i,atom_id j)
-{ e[j]=e[j] & ~(1<<i); }
-static gmx_bool ISEXCL_(t_excl e[],atom_id i,atom_id j)
-{ return (gmx_bool)(e[j] & (1<<i)); }
-static gmx_bool NOTEXCL_(t_excl e[],atom_id i,atom_id j)
-{ return !(ISEXCL(e,i,j)); }
-#else
-#define SETEXCL(e,i,j) (e)[((atom_id) (j))] |= (1<<((atom_id) (i)))
-#define RMEXCL(e,i,j) (e)[((atom_id) (j))] &= (~(1<<((atom_id) (i))))
-#define ISEXCL(e,i,j) (gmx_bool) ((e)[((atom_id) (j))] & (1<<((atom_id) (i))))
-#define NOTEXCL(e,i,j) !(ISEXCL(e,i,j))
-#endif
-
-/************************************************
- *
- * U T I L I T I E S F O R N S
- *
- ************************************************/
-
-static void reallocate_nblist(t_nblist *nl)
-{
- if (gmx_debug_at)
- {
- fprintf(debug,"reallocating neigborlist il_code=%d, maxnri=%d\n",
- nl->il_code,nl->maxnri);
- }
- srenew(nl->iinr, nl->maxnri);
- if (nl->enlist == enlistCG_CG)
- {
- srenew(nl->iinr_end,nl->maxnri);
- }
- srenew(nl->gid, nl->maxnri);
- srenew(nl->shift, nl->maxnri);
- srenew(nl->jindex, nl->maxnri+1);
-}
-
-/* ivdw/icoul are used to determine the type of interaction, so we
- * can set an innerloop index here. The obvious choice for this would have
- * been the vdwtype/coultype values in the forcerecord, but unfortunately
- * those types are braindead - for instance both Buckingham and normal
- * Lennard-Jones use the same value (evdwCUT), and a separate gmx_boolean variable
- * to determine which interaction is used. There is further no special value
- * for 'no interaction'. For backward compatibility with old TPR files we won't
- * change this in the 3.x series, so when calling this routine you should use:
- *
- * icoul=0 no coulomb interaction
- * icoul=1 cutoff standard coulomb
- * icoul=2 reaction-field coulomb
- * icoul=3 tabulated coulomb
- *
- * ivdw=0 no vdw interaction
- * ivdw=1 standard L-J interaction
- * ivdw=2 Buckingham
- * ivdw=3 tabulated vdw.
- *
- * Kind of ugly, but it works.
- */
-static void init_nblist(t_nblist *nl_sr,t_nblist *nl_lr,
- int maxsr,int maxlr,
- int ivdw, int icoul,
- gmx_bool bfree, int enlist)
-{
- t_nblist *nl;
- int homenr;
- int i,nn;
-
- int inloop[20] =
- {
- eNR_NBKERNEL_NONE,
- eNR_NBKERNEL010,
- eNR_NBKERNEL020,
- eNR_NBKERNEL030,
- eNR_NBKERNEL100,
- eNR_NBKERNEL110,
- eNR_NBKERNEL120,
- eNR_NBKERNEL130,
- eNR_NBKERNEL200,
- eNR_NBKERNEL210,
- eNR_NBKERNEL220,
- eNR_NBKERNEL230,
- eNR_NBKERNEL300,
- eNR_NBKERNEL310,
- eNR_NBKERNEL320,
- eNR_NBKERNEL330,
- eNR_NBKERNEL400,
- eNR_NBKERNEL410,
- eNR_NBKERNEL_NONE,
- eNR_NBKERNEL430
- };
-
- for(i=0; (i<2); i++)
- {
- nl = (i == 0) ? nl_sr : nl_lr;
- homenr = (i == 0) ? maxsr : maxlr;
-
- if (nl == NULL)
- {
- continue;
- }
-
- /* Set coul/vdw in neighborlist, and for the normal loops we determine
- * an index of which one to call.
- */
- nl->ivdw = ivdw;
- nl->icoul = icoul;
- nl->free_energy = bfree;
-
- if (bfree)
- {
- nl->enlist = enlistATOM_ATOM;
- nl->il_code = eNR_NBKERNEL_FREE_ENERGY;
- }
- else
- {
- nl->enlist = enlist;
-
- nn = inloop[4*icoul + ivdw];
-
- /* solvent loops follow directly after the corresponding
- * ordinary loops, in the order:
- *
- * SPC, SPC-SPC, TIP4p, TIP4p-TIP4p
- *
- */
- switch (enlist) {
- case enlistATOM_ATOM:
- case enlistCG_CG:
- break;
- case enlistSPC_ATOM: nn += 1; break;
- case enlistSPC_SPC: nn += 2; break;
- case enlistTIP4P_ATOM: nn += 3; break;
- case enlistTIP4P_TIP4P: nn += 4; break;
- }
-
- nl->il_code = nn;
- }
-
- if (debug)
- fprintf(debug,"Initiating neighbourlist type %d for %s interactions,\nwith %d SR, %d LR atoms.\n",
- nl->il_code,ENLISTTYPE(enlist),maxsr,maxlr);
-
- /* maxnri is influenced by the number of shifts (maximum is 8)
- * and the number of energy groups.
- * If it is not enough, nl memory will be reallocated during the run.
- * 4 seems to be a reasonable factor, which only causes reallocation
- * during runs with tiny and many energygroups.
- */
- nl->maxnri = homenr*4;
- nl->maxnrj = 0;
- nl->maxlen = 0;
- nl->nri = -1;
- nl->nrj = 0;
- nl->iinr = NULL;
- nl->gid = NULL;
- nl->shift = NULL;
- nl->jindex = NULL;
- reallocate_nblist(nl);
- nl->jindex[0] = 0;
-#ifdef GMX_THREAD_SHM_FDECOMP
- nl->counter = 0;
- snew(nl->mtx,1);
- pthread_mutex_init(nl->mtx,NULL);
-#endif
- }
-}
-
-void init_neighbor_list(FILE *log,t_forcerec *fr,int homenr)
-{
- /* Make maxlr tunable! (does not seem to be a big difference though)
- * This parameter determines the number of i particles in a long range
- * neighbourlist. Too few means many function calls, too many means
- * cache trashing.
- */
- int maxsr,maxsr_wat,maxlr,maxlr_wat;
- int icoul,icoulf,ivdw;
- int solvent;
- int enlist_def,enlist_w,enlist_ww;
- int i;
- t_nblists *nbl;
-
- /* maxsr = homenr-fr->nWatMol*3; */
- maxsr = homenr;
-
- if (maxsr < 0)
- {
- gmx_fatal(FARGS,"%s, %d: Negative number of short range atoms.\n"
- "Call your Gromacs dealer for assistance.",__FILE__,__LINE__);
- }
- /* This is just for initial allocation, so we do not reallocate
- * all the nlist arrays many times in a row.
- * The numbers seem very accurate, but they are uncritical.
- */
- maxsr_wat = min(fr->nWatMol,(homenr+2)/3);
- if (fr->bTwinRange)
- {
- maxlr = 50;
- maxlr_wat = min(maxsr_wat,maxlr);
- }
- else
- {
- maxlr = maxlr_wat = 0;
- }
-
- /* Determine the values for icoul/ivdw. */
- /* Start with GB */
- if(fr->bGB)
- {
- icoul=4;
- }
- else if (fr->bcoultab)
- {
- icoul = 3;
- }
- else if (EEL_RF(fr->eeltype))
- {
- icoul = 2;
- }
- else
- {
- icoul = 1;
- }
-
- if (fr->bvdwtab)
- {
- ivdw = 3;
- }
- else if (fr->bBHAM)
- {
- ivdw = 2;
- }
- else
- {
- ivdw = 1;
- }
-
- fr->ns.bCGlist = (getenv("GMX_NBLISTCG") != 0);
- if (!fr->ns.bCGlist)
- {
- enlist_def = enlistATOM_ATOM;
- }
- else
- {
- enlist_def = enlistCG_CG;
- if (log != NULL)
- {
- fprintf(log,"\nUsing charge-group - charge-group neighbor lists and kernels\n\n");
- }
- if (!fr->bExcl_IntraCGAll_InterCGNone)
- {
- gmx_fatal(FARGS,"The charge-group - charge-group force loops only support systems with all intra-cg interactions excluded and no inter-cg exclusions, this is not the case for this system.");
- }
- }
-
- if (fr->solvent_opt == esolTIP4P) {
- enlist_w = enlistTIP4P_ATOM;
- enlist_ww = enlistTIP4P_TIP4P;
- } else {
- enlist_w = enlistSPC_ATOM;
- enlist_ww = enlistSPC_SPC;
- }
-
- for(i=0; i<fr->nnblists; i++)
- {
- nbl = &(fr->nblists[i]);
- init_nblist(&nbl->nlist_sr[eNL_VDWQQ],&nbl->nlist_lr[eNL_VDWQQ],
- maxsr,maxlr,ivdw,icoul,FALSE,enlist_def);
- init_nblist(&nbl->nlist_sr[eNL_VDW],&nbl->nlist_lr[eNL_VDW],
- maxsr,maxlr,ivdw,0,FALSE,enlist_def);
- init_nblist(&nbl->nlist_sr[eNL_QQ],&nbl->nlist_lr[eNL_QQ],
- maxsr,maxlr,0,icoul,FALSE,enlist_def);
- init_nblist(&nbl->nlist_sr[eNL_VDWQQ_WATER],&nbl->nlist_lr[eNL_VDWQQ_WATER],
- maxsr_wat,maxlr_wat,ivdw,icoul, FALSE,enlist_w);
- init_nblist(&nbl->nlist_sr[eNL_QQ_WATER],&nbl->nlist_lr[eNL_QQ_WATER],
- maxsr_wat,maxlr_wat,0,icoul, FALSE,enlist_w);
- init_nblist(&nbl->nlist_sr[eNL_VDWQQ_WATERWATER],&nbl->nlist_lr[eNL_VDWQQ_WATERWATER],
- maxsr_wat,maxlr_wat,ivdw,icoul, FALSE,enlist_ww);
- init_nblist(&nbl->nlist_sr[eNL_QQ_WATERWATER],&nbl->nlist_lr[eNL_QQ_WATERWATER],
- maxsr_wat,maxlr_wat,0,icoul, FALSE,enlist_ww);
-
- if (fr->efep != efepNO)
- {
- if (fr->bEwald)
- {
- icoulf = 5;
- }
- else
- {
- icoulf = icoul;
- }
-
- init_nblist(&nbl->nlist_sr[eNL_VDWQQ_FREE],&nbl->nlist_lr[eNL_VDWQQ_FREE],
- maxsr,maxlr,ivdw,icoulf,TRUE,enlistATOM_ATOM);
- init_nblist(&nbl->nlist_sr[eNL_VDW_FREE],&nbl->nlist_lr[eNL_VDW_FREE],
- maxsr,maxlr,ivdw,0,TRUE,enlistATOM_ATOM);
- init_nblist(&nbl->nlist_sr[eNL_QQ_FREE],&nbl->nlist_lr[eNL_QQ_FREE],
- maxsr,maxlr,0,icoulf,TRUE,enlistATOM_ATOM);
- }
- }
- /* QMMM MM list */
- if (fr->bQMMM && fr->qr->QMMMscheme != eQMMMschemeoniom)
- {
- init_nblist(&fr->QMMMlist,NULL,
- maxsr,maxlr,0,icoul,FALSE,enlistATOM_ATOM);
- }
-
- fr->ns.nblist_initialized=TRUE;
-}
-
-static void reset_nblist(t_nblist *nl)
-{
- nl->nri = -1;
- nl->nrj = 0;
- nl->maxlen = 0;
- if (nl->jindex)
- {
- nl->jindex[0] = 0;
- }
-}
-
-static void reset_neighbor_list(t_forcerec *fr,gmx_bool bLR,int nls,int eNL)
-{
- int n,i;
-
- if (bLR)
- {
- reset_nblist(&(fr->nblists[nls].nlist_lr[eNL]));
- }
- else
- {
- for(n=0; n<fr->nnblists; n++)
- {
- for(i=0; i<eNL_NR; i++)
- {
- reset_nblist(&(fr->nblists[n].nlist_sr[i]));
- }
- }
- if (fr->bQMMM)
- {
- /* only reset the short-range nblist */
- reset_nblist(&(fr->QMMMlist));
- }
- }
-}
-
-
-
-
-static inline void new_i_nblist(t_nblist *nlist,
- gmx_bool bLR,atom_id i_atom,int shift,int gid)
-{
- int i,k,nri,nshift;
-
- nri = nlist->nri;
-
- /* Check whether we have to increase the i counter */
- if ((nri == -1) ||
- (nlist->iinr[nri] != i_atom) ||
- (nlist->shift[nri] != shift) ||
- (nlist->gid[nri] != gid))
- {
- /* This is something else. Now see if any entries have
- * been added in the list of the previous atom.
- */
- if ((nri == -1) ||
- ((nlist->jindex[nri+1] > nlist->jindex[nri]) &&
- (nlist->gid[nri] != -1)))
- {
- /* If so increase the counter */
- nlist->nri++;
- nri++;
- if (nlist->nri >= nlist->maxnri)
- {
- nlist->maxnri += over_alloc_large(nlist->nri);
- reallocate_nblist(nlist);
- }
- }
- /* Set the number of neighbours and the atom number */
- nlist->jindex[nri+1] = nlist->jindex[nri];
- nlist->iinr[nri] = i_atom;
- nlist->gid[nri] = gid;
- nlist->shift[nri] = shift;
- }
-}
-
-static inline void close_i_nblist(t_nblist *nlist)
-{
- int nri = nlist->nri;
- int len;
-
- if (nri >= 0)
- {
- nlist->jindex[nri+1] = nlist->nrj;
-
- len=nlist->nrj - nlist->jindex[nri];
-
- /* nlist length for water i molecules is treated statically
- * in the innerloops
- */
- if (len > nlist->maxlen)
- {
- nlist->maxlen = len;
- }
- }
-}
-
-static inline void close_nblist(t_nblist *nlist)
-{
- /* Only close this nblist when it has been initialized.
- * Avoid the creation of i-lists with no j-particles.
- */
- if (nlist->nrj == 0)
- {
- /* Some assembly kernels do not support empty lists,
- * make sure here that we don't generate any empty lists.
- * With the current ns code this branch is taken in two cases:
- * No i-particles at all: nri=-1 here
- * There are i-particles, but no j-particles; nri=0 here
- */
- nlist->nri = 0;
- }
- else
- {
- /* Close list number nri by incrementing the count */
- nlist->nri++;
- }
-}
-
-static inline void close_neighbor_list(t_forcerec *fr,gmx_bool bLR,int nls,int eNL,
- gmx_bool bMakeQMMMnblist)
-{
- int n,i;
-
- if (bMakeQMMMnblist) {
- if (!bLR)
- {
- close_nblist(&(fr->QMMMlist));
- }
- }
- else
- {
- if (bLR)
- {
- close_nblist(&(fr->nblists[nls].nlist_lr[eNL]));
- }
- else
- {
- for(n=0; n<fr->nnblists; n++)
- {
- for(i=0; (i<eNL_NR); i++)
- {
- close_nblist(&(fr->nblists[n].nlist_sr[i]));
- }
- }
- }
- }
-}
-
-static inline void add_j_to_nblist(t_nblist *nlist,atom_id j_atom,gmx_bool bLR)
-{
- int nrj=nlist->nrj;
-
- if (nlist->nrj >= nlist->maxnrj)
- {
- nlist->maxnrj = over_alloc_small(nlist->nrj + 1);
- if (gmx_debug_at)
- fprintf(debug,"Increasing %s nblist %s j size to %d\n",
- bLR ? "LR" : "SR",nrnb_str(nlist->il_code),nlist->maxnrj);
-
- srenew(nlist->jjnr,nlist->maxnrj);
- }
-
- nlist->jjnr[nrj] = j_atom;
- nlist->nrj ++;
-}
-
-static inline void add_j_to_nblist_cg(t_nblist *nlist,
- atom_id j_start,int j_end,
- t_excl *bexcl,gmx_bool bLR)
-{
- int nrj=nlist->nrj;
- int j;
-
- if (nlist->nrj >= nlist->maxnrj)
- {
- nlist->maxnrj = over_alloc_small(nlist->nrj + 1);
- if (gmx_debug_at)
- fprintf(debug,"Increasing %s nblist %s j size to %d\n",
- bLR ? "LR" : "SR",nrnb_str(nlist->il_code),nlist->maxnrj);
-
- srenew(nlist->jjnr ,nlist->maxnrj);
- srenew(nlist->jjnr_end,nlist->maxnrj);
- srenew(nlist->excl ,nlist->maxnrj*MAX_CGCGSIZE);
- }
-
- nlist->jjnr[nrj] = j_start;
- nlist->jjnr_end[nrj] = j_end;
-
- if (j_end - j_start > MAX_CGCGSIZE)
- {
- gmx_fatal(FARGS,"The charge-group - charge-group neighborlist do not support charge groups larger than %d, found a charge group of size %d",MAX_CGCGSIZE,j_end-j_start);
- }
-
- /* Set the exclusions */
- for(j=j_start; j<j_end; j++)
- {
- nlist->excl[nrj*MAX_CGCGSIZE + j - j_start] = bexcl[j];
- }
-
- nlist->nrj ++;
-}
-
-typedef void
-put_in_list_t(gmx_bool bHaveVdW[],
- int ngid,
- t_mdatoms * md,
- int icg,
- int jgid,
- int nj,
- atom_id jjcg[],
- atom_id index[],
- t_excl bExcl[],
- int shift,
- t_forcerec * fr,
- gmx_bool bLR,
- gmx_bool bDoVdW,
- gmx_bool bDoCoul);
-
-static void
-put_in_list_at(gmx_bool bHaveVdW[],
- int ngid,
- t_mdatoms * md,
- int icg,
- int jgid,
- int nj,
- atom_id jjcg[],
- atom_id index[],
- t_excl bExcl[],
- int shift,
- t_forcerec * fr,
- gmx_bool bLR,
- gmx_bool bDoVdW,
- gmx_bool bDoCoul)
-{
- /* The a[] index has been removed,
- * to put it back in i_atom should be a[i0] and jj should be a[jj].
- */
- t_nblist * vdwc;
- t_nblist * vdw;
- t_nblist * coul;
- t_nblist * vdwc_free = NULL;
- t_nblist * vdw_free = NULL;
- t_nblist * coul_free = NULL;
- t_nblist * vdwc_ww = NULL;
- t_nblist * coul_ww = NULL;
-
- int i,j,jcg,igid,gid,nbl_ind,ind_ij;
- atom_id jj,jj0,jj1,i_atom;
- int i0,nicg,len;
-
- int *cginfo;
- int *type,*typeB;
- real *charge,*chargeB;
- real qi,qiB,qq,rlj;
- gmx_bool bFreeEnergy,bFree,bFreeJ,bNotEx,*bPert;
- gmx_bool bDoVdW_i,bDoCoul_i,bDoCoul_i_sol;
- int iwater,jwater;
- t_nblist *nlist;
-
- /* Copy some pointers */
- cginfo = fr->cginfo;
- charge = md->chargeA;
- chargeB = md->chargeB;
- type = md->typeA;
- typeB = md->typeB;
- bPert = md->bPerturbed;
-
- /* Get atom range */
- i0 = index[icg];
- nicg = index[icg+1]-i0;
-
- /* Get the i charge group info */
- igid = GET_CGINFO_GID(cginfo[icg]);
- iwater = GET_CGINFO_SOLOPT(cginfo[icg]);
-
- bFreeEnergy = FALSE;
- if (md->nPerturbed)
- {
- /* Check if any of the particles involved are perturbed.
- * If not we can do the cheaper normal put_in_list
- * and use more solvent optimization.
- */
- for(i=0; i<nicg; i++)
- {
- bFreeEnergy |= bPert[i0+i];
- }
- /* Loop over the j charge groups */
- for(j=0; (j<nj && !bFreeEnergy); j++)
- {
- jcg = jjcg[j];
- jj0 = index[jcg];
- jj1 = index[jcg+1];
- /* Finally loop over the atoms in the j-charge group */
- for(jj=jj0; jj<jj1; jj++)
- {
- bFreeEnergy |= bPert[jj];
- }
- }
- }
-
- /* Unpack pointers to neighbourlist structs */
- if (fr->nnblists == 1)
- {
- nbl_ind = 0;
- }
- else
- {
- nbl_ind = fr->gid2nblists[GID(igid,jgid,ngid)];
- }
- if (bLR)
- {
- nlist = fr->nblists[nbl_ind].nlist_lr;
- }
- else
- {
- nlist = fr->nblists[nbl_ind].nlist_sr;
- }
-
- if (iwater != esolNO)
- {
- vdwc = &nlist[eNL_VDWQQ_WATER];
- vdw = &nlist[eNL_VDW];
- coul = &nlist[eNL_QQ_WATER];
-#ifndef DISABLE_WATERWATER_NLIST
- vdwc_ww = &nlist[eNL_VDWQQ_WATERWATER];
- coul_ww = &nlist[eNL_QQ_WATERWATER];
-#endif
- }
- else
- {
- vdwc = &nlist[eNL_VDWQQ];
- vdw = &nlist[eNL_VDW];
- coul = &nlist[eNL_QQ];
- }
-
- if (!bFreeEnergy)
- {
- if (iwater != esolNO)
- {
- /* Loop over the atoms in the i charge group */
- i_atom = i0;
- gid = GID(igid,jgid,ngid);
- /* Create new i_atom for each energy group */
- if (bDoCoul && bDoVdW)
- {
- new_i_nblist(vdwc,bLR,i_atom,shift,gid);
-#ifndef DISABLE_WATERWATER_NLIST
- new_i_nblist(vdwc_ww,bLR,i_atom,shift,gid);
-#endif
- }
- if (bDoVdW)
- {
- new_i_nblist(vdw,bLR,i_atom,shift,gid);
- }
- if (bDoCoul)
- {
- new_i_nblist(coul,bLR,i_atom,shift,gid);
-#ifndef DISABLE_WATERWATER_NLIST
- new_i_nblist(coul_ww,bLR,i_atom,shift,gid);
-#endif
- }
- /* Loop over the j charge groups */
- for(j=0; (j<nj); j++)
- {
- jcg=jjcg[j];
-
- if (jcg == icg)
- {
- continue;
- }
-
- jj0 = index[jcg];
- jwater = GET_CGINFO_SOLOPT(cginfo[jcg]);
-
- if (iwater == esolSPC && jwater == esolSPC)
- {
- /* Interaction between two SPC molecules */
- if (!bDoCoul)
- {
- /* VdW only - only first atoms in each water interact */
- add_j_to_nblist(vdw,jj0,bLR);
- }
- else
- {
-#ifdef DISABLE_WATERWATER_NLIST
- /* Add entries for the three atoms - only do VdW if we need to */
- if (!bDoVdW)
- {
- add_j_to_nblist(coul,jj0,bLR);
- }
- else
- {
- add_j_to_nblist(vdwc,jj0,bLR);
- }
- add_j_to_nblist(coul,jj0+1,bLR);
- add_j_to_nblist(coul,jj0+2,bLR);
-#else
- /* One entry for the entire water-water interaction */
- if (!bDoVdW)
- {
- add_j_to_nblist(coul_ww,jj0,bLR);
- }
- else
- {
- add_j_to_nblist(vdwc_ww,jj0,bLR);
- }
-#endif
- }
- }
- else if (iwater == esolTIP4P && jwater == esolTIP4P)
- {
- /* Interaction between two TIP4p molecules */
- if (!bDoCoul)
- {
- /* VdW only - only first atoms in each water interact */
- add_j_to_nblist(vdw,jj0,bLR);
- }
- else
- {
-#ifdef DISABLE_WATERWATER_NLIST
- /* Add entries for the four atoms - only do VdW if we need to */
- if (bDoVdW)
- {
- add_j_to_nblist(vdw,jj0,bLR);
- }
- add_j_to_nblist(coul,jj0+1,bLR);
- add_j_to_nblist(coul,jj0+2,bLR);
- add_j_to_nblist(coul,jj0+3,bLR);
-#else
- /* One entry for the entire water-water interaction */
- if (!bDoVdW)
- {
- add_j_to_nblist(coul_ww,jj0,bLR);
- }
- else
- {
- add_j_to_nblist(vdwc_ww,jj0,bLR);
- }
-#endif
- }
- }
- else
- {
- /* j charge group is not water, but i is.
- * Add entries to the water-other_atom lists; the geometry of the water
- * molecule doesn't matter - that is taken care of in the nonbonded kernel,
- * so we don't care if it is SPC or TIP4P...
- */
-
- jj1 = index[jcg+1];
-
- if (!bDoVdW)
- {
- for(jj=jj0; (jj<jj1); jj++)
- {
- if (charge[jj] != 0)
- {
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- }
- else if (!bDoCoul)
- {
- for(jj=jj0; (jj<jj1); jj++)
- {
- if (bHaveVdW[type[jj]])
- {
- add_j_to_nblist(vdw,jj,bLR);
- }
- }
- }
- else
- {
- /* _charge_ _groups_ interact with both coulomb and LJ */
- /* Check which atoms we should add to the lists! */
- for(jj=jj0; (jj<jj1); jj++)
- {
- if (bHaveVdW[type[jj]])
- {
- if (charge[jj] != 0)
- {
- add_j_to_nblist(vdwc,jj,bLR);
- }
- else
- {
- add_j_to_nblist(vdw,jj,bLR);
- }
- }
- else if (charge[jj] != 0)
- {
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- }
- }
- }
- close_i_nblist(vdw);
- close_i_nblist(coul);
- close_i_nblist(vdwc);
-#ifndef DISABLE_WATERWATER_NLIST
- close_i_nblist(coul_ww);
- close_i_nblist(vdwc_ww);
-#endif
- }
- else
- {
- /* no solvent as i charge group */
- /* Loop over the atoms in the i charge group */
- for(i=0; i<nicg; i++)
- {
- i_atom = i0+i;
- gid = GID(igid,jgid,ngid);
- qi = charge[i_atom];
-
- /* Create new i_atom for each energy group */
- if (bDoVdW && bDoCoul)
- {
- new_i_nblist(vdwc,bLR,i_atom,shift,gid);
- }
- if (bDoVdW)
- {
- new_i_nblist(vdw,bLR,i_atom,shift,gid);
- }
- if (bDoCoul)
- {
- new_i_nblist(coul,bLR,i_atom,shift,gid);
- }
- bDoVdW_i = (bDoVdW && bHaveVdW[type[i_atom]]);
- bDoCoul_i = (bDoCoul && qi!=0);
-
- if (bDoVdW_i || bDoCoul_i)
- {
- /* Loop over the j charge groups */
- for(j=0; (j<nj); j++)
- {
- jcg=jjcg[j];
-
- /* Check for large charge groups */
- if (jcg == icg)
- {
- jj0 = i0 + i + 1;
- }
- else
- {
- jj0 = index[jcg];
- }
-
- jj1=index[jcg+1];
- /* Finally loop over the atoms in the j-charge group */
- for(jj=jj0; jj<jj1; jj++)
- {
- bNotEx = NOTEXCL(bExcl,i,jj);
-
- if (bNotEx)
- {
- if (!bDoVdW_i)
- {
- if (charge[jj] != 0)
- {
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- else if (!bDoCoul_i)
- {
- if (bHaveVdW[type[jj]])
- {
- add_j_to_nblist(vdw,jj,bLR);
- }
- }
- else
- {
- if (bHaveVdW[type[jj]])
- {
- if (charge[jj] != 0)
- {
- add_j_to_nblist(vdwc,jj,bLR);
- }
- else
- {
- add_j_to_nblist(vdw,jj,bLR);
- }
- }
- else if (charge[jj] != 0)
- {
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- }
- }
- }
- }
- close_i_nblist(vdw);
- close_i_nblist(coul);
- close_i_nblist(vdwc);
- }
- }
- }
- else
- {
- /* we are doing free energy */
- vdwc_free = &nlist[eNL_VDWQQ_FREE];
- vdw_free = &nlist[eNL_VDW_FREE];
- coul_free = &nlist[eNL_QQ_FREE];
- /* Loop over the atoms in the i charge group */
- for(i=0; i<nicg; i++)
- {
- i_atom = i0+i;
- gid = GID(igid,jgid,ngid);
- qi = charge[i_atom];
- qiB = chargeB[i_atom];
-
- /* Create new i_atom for each energy group */
- if (bDoVdW && bDoCoul)
- new_i_nblist(vdwc,bLR,i_atom,shift,gid);
- if (bDoVdW)
- new_i_nblist(vdw,bLR,i_atom,shift,gid);
- if (bDoCoul)
- new_i_nblist(coul,bLR,i_atom,shift,gid);
-
- new_i_nblist(vdw_free,bLR,i_atom,shift,gid);
- new_i_nblist(coul_free,bLR,i_atom,shift,gid);
- new_i_nblist(vdwc_free,bLR,i_atom,shift,gid);
-
- bDoVdW_i = (bDoVdW &&
- (bHaveVdW[type[i_atom]] || bHaveVdW[typeB[i_atom]]));
- bDoCoul_i = (bDoCoul && (qi!=0 || qiB!=0));
- /* For TIP4P the first atom does not have a charge,
- * but the last three do. So we should still put an atom
- * without LJ but with charge in the water-atom neighborlist
- * for a TIP4p i charge group.
- * For SPC type water the first atom has LJ and charge,
- * so there is no such problem.
- */
- if (iwater == esolNO)
- {
- bDoCoul_i_sol = bDoCoul_i;
- }
- else
- {
- bDoCoul_i_sol = bDoCoul;
- }
-
- if (bDoVdW_i || bDoCoul_i_sol)
- {
- /* Loop over the j charge groups */
- for(j=0; (j<nj); j++)
- {
- jcg=jjcg[j];
-
- /* Check for large charge groups */
- if (jcg == icg)
- {
- jj0 = i0 + i + 1;
- }
- else
- {
- jj0 = index[jcg];
- }
-
- jj1=index[jcg+1];
- /* Finally loop over the atoms in the j-charge group */
- bFree = bPert[i_atom];
- for(jj=jj0; (jj<jj1); jj++)
- {
- bFreeJ = bFree || bPert[jj];
- /* Complicated if, because the water H's should also
- * see perturbed j-particles
- */
- if (iwater==esolNO || i==0 || bFreeJ)
- {
- bNotEx = NOTEXCL(bExcl,i,jj);
-
- if (bNotEx)
- {
- if (bFreeJ)
- {
- if (!bDoVdW_i)
- {
- if (charge[jj]!=0 || chargeB[jj]!=0)
- {
- add_j_to_nblist(coul_free,jj,bLR);
- }
- }
- else if (!bDoCoul_i)
- {
- if (bHaveVdW[type[jj]] || bHaveVdW[typeB[jj]])
- {
- add_j_to_nblist(vdw_free,jj,bLR);
- }
- }
- else
- {
- if (bHaveVdW[type[jj]] || bHaveVdW[typeB[jj]])
- {
- if (charge[jj]!=0 || chargeB[jj]!=0)
- {
- add_j_to_nblist(vdwc_free,jj,bLR);
- }
- else
- {
- add_j_to_nblist(vdw_free,jj,bLR);
- }
- }
- else if (charge[jj]!=0 || chargeB[jj]!=0)
- add_j_to_nblist(coul_free,jj,bLR);
- }
- }
- else if (!bDoVdW_i)
- {
- /* This is done whether or not bWater is set */
- if (charge[jj] != 0)
- {
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- else if (!bDoCoul_i_sol)
- {
- if (bHaveVdW[type[jj]])
- {
- add_j_to_nblist(vdw,jj,bLR);
- }
- }
- else
- {
- if (bHaveVdW[type[jj]])
- {
- if (charge[jj] != 0)
- {
- add_j_to_nblist(vdwc,jj,bLR);
- }
- else
- {
- add_j_to_nblist(vdw,jj,bLR);
- }
- }
- else if (charge[jj] != 0)
- {
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- }
- }
- }
- }
- }
- close_i_nblist(vdw);
- close_i_nblist(coul);
- close_i_nblist(vdwc);
- close_i_nblist(vdw_free);
- close_i_nblist(coul_free);
- close_i_nblist(vdwc_free);
- }
- }
-}
-
-static void
-put_in_list_qmmm(gmx_bool bHaveVdW[],
- int ngid,
- t_mdatoms * md,
- int icg,
- int jgid,
- int nj,
- atom_id jjcg[],
- atom_id index[],
- t_excl bExcl[],
- int shift,
- t_forcerec * fr,
- gmx_bool bLR,
- gmx_bool bDoVdW,
- gmx_bool bDoCoul)
-{
- t_nblist * coul;
- int i,j,jcg,igid,gid;
- atom_id jj,jj0,jj1,i_atom;
- int i0,nicg;
- gmx_bool bNotEx;
-
- /* Get atom range */
- i0 = index[icg];
- nicg = index[icg+1]-i0;
-
- /* Get the i charge group info */
- igid = GET_CGINFO_GID(fr->cginfo[icg]);
-
- coul = &fr->QMMMlist;
-
- /* Loop over atoms in the ith charge group */
- for (i=0;i<nicg;i++)
- {
- i_atom = i0+i;
- gid = GID(igid,jgid,ngid);
- /* Create new i_atom for each energy group */
- new_i_nblist(coul,bLR,i_atom,shift,gid);
-
- /* Loop over the j charge groups */
- for (j=0;j<nj;j++)
- {
- jcg=jjcg[j];
-
- /* Charge groups cannot have QM and MM atoms simultaneously */
- if (jcg!=icg)
- {
- jj0 = index[jcg];
- jj1 = index[jcg+1];
- /* Finally loop over the atoms in the j-charge group */
- for(jj=jj0; jj<jj1; jj++)
- {
- bNotEx = NOTEXCL(bExcl,i,jj);
- if(bNotEx)
- add_j_to_nblist(coul,jj,bLR);
- }
- }
- }
- close_i_nblist(coul);
- }
-}
-
-static void
-put_in_list_cg(gmx_bool bHaveVdW[],
- int ngid,
- t_mdatoms * md,
- int icg,
- int jgid,
- int nj,
- atom_id jjcg[],
- atom_id index[],
- t_excl bExcl[],
- int shift,
- t_forcerec * fr,
- gmx_bool bLR,
- gmx_bool bDoVdW,
- gmx_bool bDoCoul)
-{
- int cginfo;
- int igid,gid,nbl_ind;
- t_nblist * vdwc;
- int j,jcg;
-
- cginfo = fr->cginfo[icg];
-
- igid = GET_CGINFO_GID(cginfo);
- gid = GID(igid,jgid,ngid);
-
- /* Unpack pointers to neighbourlist structs */
- if (fr->nnblists == 1)
- {
- nbl_ind = 0;
- }
- else
- {
- nbl_ind = fr->gid2nblists[gid];
- }
- if (bLR)
- {
- vdwc = &fr->nblists[nbl_ind].nlist_lr[eNL_VDWQQ];
- }
- else
- {
- vdwc = &fr->nblists[nbl_ind].nlist_sr[eNL_VDWQQ];
- }
-
- /* Make a new neighbor list for charge group icg.
- * Currently simply one neighbor list is made with LJ and Coulomb.
- * If required, zero interactions could be removed here
- * or in the force loop.
- */
- new_i_nblist(vdwc,bLR,index[icg],shift,gid);
- vdwc->iinr_end[vdwc->nri] = index[icg+1];
-
- for(j=0; (j<nj); j++)
- {
- jcg = jjcg[j];
- /* Skip the icg-icg pairs if all self interactions are excluded */
- if (!(jcg == icg && GET_CGINFO_EXCL_INTRA(cginfo)))
- {
- /* Here we add the j charge group jcg to the list,
- * exclusions are also added to the list.
- */
- add_j_to_nblist_cg(vdwc,index[jcg],index[jcg+1],bExcl,bLR);
- }
- }
-
- close_i_nblist(vdwc);
-}
-
-static void setexcl(atom_id start,atom_id end,t_blocka *excl,gmx_bool b,
- t_excl bexcl[])
-{
- atom_id i,k;
-
- if (b)
- {
- for(i=start; i<end; i++)
- {
- for(k=excl->index[i]; k<excl->index[i+1]; k++)
- {
- SETEXCL(bexcl,i-start,excl->a[k]);
- }
- }
- }
- else
- {
- for(i=start; i<end; i++)
- {
- for(k=excl->index[i]; k<excl->index[i+1]; k++)
- {
- RMEXCL(bexcl,i-start,excl->a[k]);
- }
- }
- }
-}
-
-int calc_naaj(int icg,int cgtot)
-{
- int naaj;
-
- if ((cgtot % 2) == 1)
- {
- /* Odd number of charge groups, easy */
- naaj = 1 + (cgtot/2);
- }
- else if ((cgtot % 4) == 0)
- {
- /* Multiple of four is hard */
- if (icg < cgtot/2)
- {
- if ((icg % 2) == 0)
- {
- naaj=1+(cgtot/2);
- }
- else
- {
- naaj=cgtot/2;
- }
- }
- else
- {
- if ((icg % 2) == 1)
- {
- naaj=1+(cgtot/2);
- }
- else
- {
- naaj=cgtot/2;
- }
- }
- }
- else
- {
- /* cgtot/2 = odd */
- if ((icg % 2) == 0)
- {
- naaj=1+(cgtot/2);
- }
- else
- {
- naaj=cgtot/2;
- }
- }
-#ifdef DEBUG
- fprintf(log,"naaj=%d\n",naaj);
-#endif
-
- return naaj;
-}
-
-/************************************************
- *
- * S I M P L E C O R E S T U F F
- *
- ************************************************/
-
-static real calc_image_tric(rvec xi,rvec xj,matrix box,
- rvec b_inv,int *shift)
-{
- /* This code assumes that the cut-off is smaller than
- * a half times the smallest diagonal element of the box.
- */
- const real h25=2.5;
- real dx,dy,dz;
- real r2;
- int tx,ty,tz;
-
- /* Compute diff vector */
- dz = xj[ZZ] - xi[ZZ];
- dy = xj[YY] - xi[YY];
- dx = xj[XX] - xi[XX];
-
- /* Perform NINT operation, using trunc operation, therefore
- * we first add 2.5 then subtract 2 again
- */
- tz = dz*b_inv[ZZ] + h25;
- tz -= 2;
- dz -= tz*box[ZZ][ZZ];
- dy -= tz*box[ZZ][YY];
- dx -= tz*box[ZZ][XX];
-
- ty = dy*b_inv[YY] + h25;
- ty -= 2;
- dy -= ty*box[YY][YY];
- dx -= ty*box[YY][XX];
-
- tx = dx*b_inv[XX]+h25;
- tx -= 2;
- dx -= tx*box[XX][XX];
-
- /* Distance squared */
- r2 = (dx*dx) + (dy*dy) + (dz*dz);
-
- *shift = XYZ2IS(tx,ty,tz);
-
- return r2;
-}
-
-static real calc_image_rect(rvec xi,rvec xj,rvec box_size,
- rvec b_inv,int *shift)
-{
- const real h15=1.5;
- real ddx,ddy,ddz;
- real dx,dy,dz;
- real r2;
- int tx,ty,tz;
-
- /* Compute diff vector */
- dx = xj[XX] - xi[XX];
- dy = xj[YY] - xi[YY];
- dz = xj[ZZ] - xi[ZZ];
-
- /* Perform NINT operation, using trunc operation, therefore
- * we first add 1.5 then subtract 1 again
- */
- tx = dx*b_inv[XX] + h15;
- ty = dy*b_inv[YY] + h15;
- tz = dz*b_inv[ZZ] + h15;
- tx--;
- ty--;
- tz--;
-
- /* Correct diff vector for translation */
- ddx = tx*box_size[XX] - dx;
- ddy = ty*box_size[YY] - dy;
- ddz = tz*box_size[ZZ] - dz;
-
- /* Distance squared */
- r2 = (ddx*ddx) + (ddy*ddy) + (ddz*ddz);
-
- *shift = XYZ2IS(tx,ty,tz);
-
- return r2;
-}
-
-static void add_simple(t_ns_buf *nsbuf,int nrj,atom_id cg_j,
- gmx_bool bHaveVdW[],int ngid,t_mdatoms *md,
- int icg,int jgid,t_block *cgs,t_excl bexcl[],
- int shift,t_forcerec *fr,put_in_list_t *put_in_list)
-{
- if (nsbuf->nj + nrj > MAX_CG)
- {
- put_in_list(bHaveVdW,ngid,md,icg,jgid,nsbuf->ncg,nsbuf->jcg,
- cgs->index,bexcl,shift,fr,FALSE,TRUE,TRUE);
- /* Reset buffer contents */
- nsbuf->ncg = nsbuf->nj = 0;
- }
- nsbuf->jcg[nsbuf->ncg++] = cg_j;
- nsbuf->nj += nrj;
-}
-
-static void ns_inner_tric(rvec x[],int icg,int *i_egp_flags,
- int njcg,atom_id jcg[],
- matrix box,rvec b_inv,real rcut2,
- t_block *cgs,t_ns_buf **ns_buf,
- gmx_bool bHaveVdW[],int ngid,t_mdatoms *md,
- t_excl bexcl[],t_forcerec *fr,
- put_in_list_t *put_in_list)
-{
- int shift;
- int j,nrj,jgid;
- int *cginfo=fr->cginfo;
- atom_id cg_j,*cgindex;
- t_ns_buf *nsbuf;
-
- cgindex = cgs->index;
- shift = CENTRAL;
- for(j=0; (j<njcg); j++)
- {
- cg_j = jcg[j];
- nrj = cgindex[cg_j+1]-cgindex[cg_j];
- if (calc_image_tric(x[icg],x[cg_j],box,b_inv,&shift) < rcut2)
- {
- jgid = GET_CGINFO_GID(cginfo[cg_j]);
- if (!(i_egp_flags[jgid] & EGP_EXCL))
- {
- add_simple(&ns_buf[jgid][shift],nrj,cg_j,
- bHaveVdW,ngid,md,icg,jgid,cgs,bexcl,shift,fr,
- put_in_list);
- }
- }
- }
-}
-
-static void ns_inner_rect(rvec x[],int icg,int *i_egp_flags,
- int njcg,atom_id jcg[],
- gmx_bool bBox,rvec box_size,rvec b_inv,real rcut2,
- t_block *cgs,t_ns_buf **ns_buf,
- gmx_bool bHaveVdW[],int ngid,t_mdatoms *md,
- t_excl bexcl[],t_forcerec *fr,
- put_in_list_t *put_in_list)
-{
- int shift;
- int j,nrj,jgid;
- int *cginfo=fr->cginfo;
- atom_id cg_j,*cgindex;
- t_ns_buf *nsbuf;
-
- cgindex = cgs->index;
- if (bBox)
- {
- shift = CENTRAL;
- for(j=0; (j<njcg); j++)
- {
- cg_j = jcg[j];
- nrj = cgindex[cg_j+1]-cgindex[cg_j];
- if (calc_image_rect(x[icg],x[cg_j],box_size,b_inv,&shift) < rcut2)
- {
- jgid = GET_CGINFO_GID(cginfo[cg_j]);
- if (!(i_egp_flags[jgid] & EGP_EXCL))
- {
- add_simple(&ns_buf[jgid][shift],nrj,cg_j,
- bHaveVdW,ngid,md,icg,jgid,cgs,bexcl,shift,fr,
- put_in_list);
- }
- }
- }
- }
- else
- {
- for(j=0; (j<njcg); j++)
- {
- cg_j = jcg[j];
- nrj = cgindex[cg_j+1]-cgindex[cg_j];
- if ((rcut2 == 0) || (distance2(x[icg],x[cg_j]) < rcut2)) {
- jgid = GET_CGINFO_GID(cginfo[cg_j]);
- if (!(i_egp_flags[jgid] & EGP_EXCL))
- {
- add_simple(&ns_buf[jgid][CENTRAL],nrj,cg_j,
- bHaveVdW,ngid,md,icg,jgid,cgs,bexcl,CENTRAL,fr,
- put_in_list);
- }
- }
- }
- }
-}
-
-/* ns_simple_core needs to be adapted for QMMM still 2005 */
-
-static int ns_simple_core(t_forcerec *fr,
- gmx_localtop_t *top,
- t_mdatoms *md,
- matrix box,rvec box_size,
- t_excl bexcl[],atom_id *aaj,
- int ngid,t_ns_buf **ns_buf,
- put_in_list_t *put_in_list,gmx_bool bHaveVdW[])
-{
- int naaj,k;
- real rlist2;
- int nsearch,icg,jcg,igid,i0,nri,nn;
- int *cginfo;
- t_ns_buf *nsbuf;
- /* atom_id *i_atoms; */
- t_block *cgs=&(top->cgs);
- t_blocka *excl=&(top->excls);
- rvec b_inv;
- int m;
- gmx_bool bBox,bTriclinic;
- int *i_egp_flags;
-
- rlist2 = sqr(fr->rlist);
-
- bBox = (fr->ePBC != epbcNONE);
- if (bBox)
- {
- for(m=0; (m<DIM); m++)
- {
- b_inv[m] = divide(1.0,box_size[m]);
- }
- bTriclinic = TRICLINIC(box);
- }
- else
- {
- bTriclinic = FALSE;
- }
-
- cginfo = fr->cginfo;
-
- nsearch=0;
- for (icg=fr->cg0; (icg<fr->hcg); icg++)
- {
- /*
- i0 = cgs->index[icg];
- nri = cgs->index[icg+1]-i0;
- i_atoms = &(cgs->a[i0]);
- i_eg_excl = fr->eg_excl + ngid*md->cENER[*i_atoms];
- setexcl(nri,i_atoms,excl,TRUE,bexcl);
- */
- igid = GET_CGINFO_GID(cginfo[icg]);
- i_egp_flags = fr->egp_flags + ngid*igid;
- setexcl(cgs->index[icg],cgs->index[icg+1],excl,TRUE,bexcl);
-
- naaj=calc_naaj(icg,cgs->nr);
- if (bTriclinic)
- {
- ns_inner_tric(fr->cg_cm,icg,i_egp_flags,naaj,&(aaj[icg]),
- box,b_inv,rlist2,cgs,ns_buf,
- bHaveVdW,ngid,md,bexcl,fr,put_in_list);
- }
- else
- {
- ns_inner_rect(fr->cg_cm,icg,i_egp_flags,naaj,&(aaj[icg]),
- bBox,box_size,b_inv,rlist2,cgs,ns_buf,
- bHaveVdW,ngid,md,bexcl,fr,put_in_list);
- }
- nsearch += naaj;
-
- for(nn=0; (nn<ngid); nn++)
- {
- for(k=0; (k<SHIFTS); k++)
- {
- nsbuf = &(ns_buf[nn][k]);
- if (nsbuf->ncg > 0)
- {
- put_in_list(bHaveVdW,ngid,md,icg,nn,nsbuf->ncg,nsbuf->jcg,
- cgs->index,bexcl,k,fr,FALSE,TRUE,TRUE);
- nsbuf->ncg=nsbuf->nj=0;
- }
- }
- }
- /* setexcl(nri,i_atoms,excl,FALSE,bexcl); */
- setexcl(cgs->index[icg],cgs->index[icg+1],excl,FALSE,bexcl);
- }
- close_neighbor_list(fr,FALSE,-1,-1,FALSE);
-
- return nsearch;
-}
-
-/************************************************
- *
- * N S 5 G R I D S T U F F
- *
- ************************************************/
-
-static inline void get_dx(int Nx,real gridx,real rc2,int xgi,real x,
- int *dx0,int *dx1,real *dcx2)
-{
- real dcx,tmp;
- int xgi0,xgi1,i;
-
- if (xgi < 0)
- {
- *dx0 = 0;
- xgi0 = -1;
- *dx1 = -1;
- xgi1 = 0;
- }
- else if (xgi >= Nx)
- {
- *dx0 = Nx;
- xgi0 = Nx-1;
- *dx1 = Nx-1;
- xgi1 = Nx;
- }
- else
- {
- dcx2[xgi] = 0;
- *dx0 = xgi;
- xgi0 = xgi-1;
- *dx1 = xgi;
- xgi1 = xgi+1;
- }
-
- for(i=xgi0; i>=0; i--)
- {
- dcx = (i+1)*gridx-x;
- tmp = dcx*dcx;
- if (tmp >= rc2)
- break;
- *dx0 = i;
- dcx2[i] = tmp;
- }
- for(i=xgi1; i<Nx; i++)
- {
- dcx = i*gridx-x;
- tmp = dcx*dcx;
- if (tmp >= rc2)
- {
- break;
- }
- *dx1 = i;
- dcx2[i] = tmp;
- }
-}
-
-static inline void get_dx_dd(int Nx,real gridx,real rc2,int xgi,real x,
- int ncpddc,int shift_min,int shift_max,
- int *g0,int *g1,real *dcx2)
-{
- real dcx,tmp;
- int g_min,g_max,shift_home;
-
- if (xgi < 0)
- {
- g_min = 0;
- g_max = Nx - 1;
- *g0 = 0;
- *g1 = -1;
- }
- else if (xgi >= Nx)
- {
- g_min = 0;
- g_max = Nx - 1;
- *g0 = Nx;
- *g1 = Nx - 1;
- }
- else
- {
- if (ncpddc == 0)
- {
- g_min = 0;
- g_max = Nx - 1;
- }
- else
- {
- if (xgi < ncpddc)
- {
- shift_home = 0;
- }
- else
- {
- shift_home = -1;
- }
- g_min = (shift_min == shift_home ? 0 : ncpddc);
- g_max = (shift_max == shift_home ? ncpddc - 1 : Nx - 1);
- }
- if (shift_min > 0)
- {
- *g0 = g_min;
- *g1 = g_min - 1;
- }
- else if (shift_max < 0)
- {
- *g0 = g_max + 1;
- *g1 = g_max;
- }
- else
- {
- *g0 = xgi;
- *g1 = xgi;
- dcx2[xgi] = 0;
- }
- }
-
- while (*g0 > g_min)
- {
- /* Check one grid cell down */
- dcx = ((*g0 - 1) + 1)*gridx - x;
- tmp = dcx*dcx;
- if (tmp >= rc2)
- {
- break;
- }
- (*g0)--;
- dcx2[*g0] = tmp;
- }
-
- while (*g1 < g_max)
- {
- /* Check one grid cell up */
- dcx = (*g1 + 1)*gridx - x;
- tmp = dcx*dcx;
- if (tmp >= rc2)
- {
- break;
- }
- (*g1)++;
- dcx2[*g1] = tmp;
- }
-}
-
-
-#define sqr(x) ((x)*(x))
-#define calc_dx2(XI,YI,ZI,y) (sqr(XI-y[XX]) + sqr(YI-y[YY]) + sqr(ZI-y[ZZ]))
-#define calc_cyl_dx2(XI,YI,y) (sqr(XI-y[XX]) + sqr(YI-y[YY]))
-/****************************************************
- *
- * F A S T N E I G H B O R S E A R C H I N G
- *
- * Optimized neighboursearching routine using grid
- * at least 1x1x1, see GROMACS manual
- *
- ****************************************************/
-
-static void do_longrange(t_commrec *cr,gmx_localtop_t *top,t_forcerec *fr,
- int ngid,t_mdatoms *md,int icg,
- int jgid,int nlr,
- atom_id lr[],t_excl bexcl[],int shift,
- rvec x[],rvec box_size,t_nrnb *nrnb,
- real lambda,real *dvdlambda,
- gmx_grppairener_t *grppener,
- gmx_bool bDoVdW,gmx_bool bDoCoul,
- gmx_bool bEvaluateNow,put_in_list_t *put_in_list,
- gmx_bool bHaveVdW[],
- gmx_bool bDoForces,rvec *f)
-{
- int n,i;
- t_nblist *nl;
-
- for(n=0; n<fr->nnblists; n++)
- {
- for(i=0; (i<eNL_NR); i++)
- {
- nl = &fr->nblists[n].nlist_lr[i];
- if ((nl->nri > nl->maxnri-32) || bEvaluateNow)
- {
- close_neighbor_list(fr,TRUE,n,i,FALSE);
- /* Evaluate the energies and forces */
- do_nonbonded(cr,fr,x,f,md,NULL,
- grppener->ener[fr->bBHAM ? egBHAMLR : egLJLR],
- grppener->ener[egCOULLR],
- grppener->ener[egGB],box_size,
- nrnb,lambda,dvdlambda,n,i,
- GMX_DONB_LR | GMX_DONB_FORCES);
-
- reset_neighbor_list(fr,TRUE,n,i);
- }
- }
- }
-
- if (!bEvaluateNow)
- {
- /* Put the long range particles in a list */
- /* do_longrange is never called for QMMM */
- put_in_list(bHaveVdW,ngid,md,icg,jgid,nlr,lr,top->cgs.index,
- bexcl,shift,fr,TRUE,bDoVdW,bDoCoul);
- }
-}
-
-static void get_cutoff2(t_forcerec *fr,gmx_bool bDoLongRange,
- real *rvdw2,real *rcoul2,
- real *rs2,real *rm2,real *rl2)
-{
- *rs2 = sqr(fr->rlist);
- if (bDoLongRange && fr->bTwinRange)
- {
- /* The VdW and elec. LR cut-off's could be different,
- * so we can not simply set them to rlistlong.
- */
- if (EVDW_MIGHT_BE_ZERO_AT_CUTOFF(fr->vdwtype) &&
- fr->rvdw > fr->rlist)
- {
- *rvdw2 = sqr(fr->rlistlong);
- }
- else
- {
- *rvdw2 = sqr(fr->rvdw);
- }
- if (EEL_MIGHT_BE_ZERO_AT_CUTOFF(fr->eeltype) &&
- fr->rcoulomb > fr->rlist)
- {
- *rcoul2 = sqr(fr->rlistlong);
- }
- else
- {
- *rcoul2 = sqr(fr->rcoulomb);
- }
- }
- else
- {
- /* Workaround for a gcc -O3 or -ffast-math problem */
- *rvdw2 = *rs2;
- *rcoul2 = *rs2;
- }
- *rm2 = min(*rvdw2,*rcoul2);
- *rl2 = max(*rvdw2,*rcoul2);
-}
-
-static void init_nsgrid_lists(t_forcerec *fr,int ngid,gmx_ns_t *ns)
-{
- real rvdw2,rcoul2,rs2,rm2,rl2;
- int j;
-
- get_cutoff2(fr,TRUE,&rvdw2,&rcoul2,&rs2,&rm2,&rl2);
-
- /* Short range buffers */
- snew(ns->nl_sr,ngid);
- /* Counters */
- snew(ns->nsr,ngid);
- snew(ns->nlr_ljc,ngid);
- snew(ns->nlr_one,ngid);
-
- if (rm2 > rs2)
- {
- /* Long range VdW and Coul buffers */
- snew(ns->nl_lr_ljc,ngid);
- }
- if (rl2 > rm2)
- {
- /* Long range VdW or Coul only buffers */
- snew(ns->nl_lr_one,ngid);
- }
- for(j=0; (j<ngid); j++) {
- snew(ns->nl_sr[j],MAX_CG);
- if (rm2 > rs2)
- {
- snew(ns->nl_lr_ljc[j],MAX_CG);
- }
- if (rl2 > rm2)
- {
- snew(ns->nl_lr_one[j],MAX_CG);
- }
- }
- if (debug)
- {
- fprintf(debug,
- "ns5_core: rs2 = %g, rm2 = %g, rl2 = %g (nm^2)\n",
- rs2,rm2,rl2);
- }
-}
-
-static int nsgrid_core(FILE *log,t_commrec *cr,t_forcerec *fr,
- matrix box,rvec box_size,int ngid,
- gmx_localtop_t *top,
- t_grid *grid,rvec x[],
- t_excl bexcl[],gmx_bool *bExcludeAlleg,
- t_nrnb *nrnb,t_mdatoms *md,
- real lambda,real *dvdlambda,
- gmx_grppairener_t *grppener,
- put_in_list_t *put_in_list,
- gmx_bool bHaveVdW[],
- gmx_bool bDoLongRange,gmx_bool bDoForces,rvec *f,
- gmx_bool bMakeQMMMnblist)
-{
- gmx_ns_t *ns;
- atom_id **nl_lr_ljc,**nl_lr_one,**nl_sr;
- int *nlr_ljc,*nlr_one,*nsr;
- gmx_domdec_t *dd=NULL;
- t_block *cgs=&(top->cgs);
- int *cginfo=fr->cginfo;
- /* atom_id *i_atoms,*cgsindex=cgs->index; */
- ivec sh0,sh1,shp;
- int cell_x,cell_y,cell_z;
- int d,tx,ty,tz,dx,dy,dz,cj;
-#ifdef ALLOW_OFFDIAG_LT_HALFDIAG
- int zsh_ty,zsh_tx,ysh_tx;
-#endif
- int dx0,dx1,dy0,dy1,dz0,dz1;
- int Nx,Ny,Nz,shift=-1,j,nrj,nns,nn=-1;
- real gridx,gridy,gridz,grid_x,grid_y,grid_z;
- real *dcx2,*dcy2,*dcz2;
- int zgi,ygi,xgi;
- int cg0,cg1,icg=-1,cgsnr,i0,igid,nri,naaj,max_jcg;
- int jcg0,jcg1,jjcg,cgj0,jgid;
- int *grida,*gridnra,*gridind;
- gmx_bool rvdw_lt_rcoul,rcoul_lt_rvdw;
- rvec xi,*cgcm,grid_offset;
- real r2,rs2,rvdw2,rcoul2,rm2,rl2,XI,YI,ZI,dcx,dcy,dcz,tmp1,tmp2;
- int *i_egp_flags;
- gmx_bool bDomDec,bTriclinicX,bTriclinicY;
- ivec ncpddc;
-
- ns = &fr->ns;
-
- bDomDec = DOMAINDECOMP(cr);
- if (bDomDec)
- {
- dd = cr->dd;
- }
-
- bTriclinicX = ((YY < grid->npbcdim &&
- (!bDomDec || dd->nc[YY]==1) && box[YY][XX] != 0) ||
- (ZZ < grid->npbcdim &&
- (!bDomDec || dd->nc[ZZ]==1) && box[ZZ][XX] != 0));
- bTriclinicY = (ZZ < grid->npbcdim &&
- (!bDomDec || dd->nc[ZZ]==1) && box[ZZ][YY] != 0);
-
- cgsnr = cgs->nr;
-
- get_cutoff2(fr,bDoLongRange,&rvdw2,&rcoul2,&rs2,&rm2,&rl2);
-
- rvdw_lt_rcoul = (rvdw2 >= rcoul2);
- rcoul_lt_rvdw = (rcoul2 >= rvdw2);
-
- if (bMakeQMMMnblist)
- {
- rm2 = rl2;
- rs2 = rl2;
- }
-
- nl_sr = ns->nl_sr;
- nsr = ns->nsr;
- nl_lr_ljc = ns->nl_lr_ljc;
- nl_lr_one = ns->nl_lr_one;
- nlr_ljc = ns->nlr_ljc;
- nlr_one = ns->nlr_one;
-
- /* Unpack arrays */
- cgcm = fr->cg_cm;
- Nx = grid->n[XX];
- Ny = grid->n[YY];
- Nz = grid->n[ZZ];
- grida = grid->a;
- gridind = grid->index;
- gridnra = grid->nra;
- nns = 0;
-
- gridx = grid->cell_size[XX];
- gridy = grid->cell_size[YY];
- gridz = grid->cell_size[ZZ];
- grid_x = 1/gridx;
- grid_y = 1/gridy;
- grid_z = 1/gridz;
- copy_rvec(grid->cell_offset,grid_offset);
- copy_ivec(grid->ncpddc,ncpddc);
- dcx2 = grid->dcx2;
- dcy2 = grid->dcy2;
- dcz2 = grid->dcz2;
-
-#ifdef ALLOW_OFFDIAG_LT_HALFDIAG
- zsh_ty = floor(-box[ZZ][YY]/box[YY][YY]+0.5);
- zsh_tx = floor(-box[ZZ][XX]/box[XX][XX]+0.5);
- ysh_tx = floor(-box[YY][XX]/box[XX][XX]+0.5);
- if (zsh_tx!=0 && ysh_tx!=0)
- {
- /* This could happen due to rounding, when both ratios are 0.5 */
- ysh_tx = 0;
- }
-#endif
-
- debug_gmx();
-
- if (fr->n_tpi)
- {
- /* We only want a list for the test particle */
- cg0 = cgsnr - 1;
- }
- else
- {
- cg0 = grid->icg0;
- }
- cg1 = grid->icg1;
-
- /* Set the shift range */
- for(d=0; d<DIM; d++)
- {
- sh0[d] = -1;
- sh1[d] = 1;
- /* Check if we need periodicity shifts.
- * Without PBC or with domain decomposition we don't need them.
- */
- if (d >= ePBC2npbcdim(fr->ePBC) || (bDomDec && dd->nc[d] > 1))
- {
- shp[d] = 0;
- }
- else
- {
- if (d == XX &&
- box[XX][XX] - fabs(box[YY][XX]) - fabs(box[ZZ][XX]) < sqrt(rl2))
- {
- shp[d] = 2;
- }
- else
- {
- shp[d] = 1;
- }
- }
- }
-
- /* Loop over charge groups */
- for(icg=cg0; (icg < cg1); icg++)
- {
- igid = GET_CGINFO_GID(cginfo[icg]);
- /* Skip this charge group if all energy groups are excluded! */
- if (bExcludeAlleg[igid])
- {
- continue;
- }
-
- i0 = cgs->index[icg];
-
- if (bMakeQMMMnblist)
- {
- /* Skip this charge group if it is not a QM atom while making a
- * QM/MM neighbourlist
- */
- if (md->bQM[i0]==FALSE)
- {
- continue; /* MM particle, go to next particle */
- }
-
- /* Compute the number of charge groups that fall within the control
- * of this one (icg)
- */
- naaj = calc_naaj(icg,cgsnr);
- jcg0 = icg;
- jcg1 = icg + naaj;
- max_jcg = cgsnr;
- }
- else
- {
- /* make a normal neighbourlist */
-
- if (bDomDec)
- {
- /* Get the j charge-group and dd cell shift ranges */
- dd_get_ns_ranges(cr->dd,icg,&jcg0,&jcg1,sh0,sh1);
- max_jcg = 0;
- }
- else
- {
- /* Compute the number of charge groups that fall within the control
- * of this one (icg)
- */
- naaj = calc_naaj(icg,cgsnr);
- jcg0 = icg;
- jcg1 = icg + naaj;
-
- if (fr->n_tpi)
- {
- /* The i-particle is awlways the test particle,
- * so we want all j-particles
- */
- max_jcg = cgsnr - 1;
- }
- else
- {
- max_jcg = jcg1 - cgsnr;
- }
- }
- }
-
- i_egp_flags = fr->egp_flags + igid*ngid;
-
- /* Set the exclusions for the atoms in charge group icg using a bitmask */
- setexcl(i0,cgs->index[icg+1],&top->excls,TRUE,bexcl);
-
- ci2xyz(grid,icg,&cell_x,&cell_y,&cell_z);
-
- /* Changed iicg to icg, DvdS 990115
- * (but see consistency check above, DvdS 990330)
- */
-#ifdef NS5DB
- fprintf(log,"icg=%5d, naaj=%5d, cell %d %d %d\n",
- icg,naaj,cell_x,cell_y,cell_z);
-#endif
- /* Loop over shift vectors in three dimensions */
- for (tz=-shp[ZZ]; tz<=shp[ZZ]; tz++)
- {
- ZI = cgcm[icg][ZZ]+tz*box[ZZ][ZZ];
- /* Calculate range of cells in Z direction that have the shift tz */
- zgi = cell_z + tz*Nz;
-#define FAST_DD_NS
-#ifndef FAST_DD_NS
- get_dx(Nz,gridz,rl2,zgi,ZI,&dz0,&dz1,dcz2);
-#else
- get_dx_dd(Nz,gridz,rl2,zgi,ZI-grid_offset[ZZ],
- ncpddc[ZZ],sh0[ZZ],sh1[ZZ],&dz0,&dz1,dcz2);
-#endif
- if (dz0 > dz1)
- {
- continue;
- }
- for (ty=-shp[YY]; ty<=shp[YY]; ty++)
- {
- YI = cgcm[icg][YY]+ty*box[YY][YY]+tz*box[ZZ][YY];
- /* Calculate range of cells in Y direction that have the shift ty */
- if (bTriclinicY)
- {
- ygi = (int)(Ny + (YI - grid_offset[YY])*grid_y) - Ny;
- }
- else
- {
- ygi = cell_y + ty*Ny;
- }
-#ifndef FAST_DD_NS
- get_dx(Ny,gridy,rl2,ygi,YI,&dy0,&dy1,dcy2);
-#else
- get_dx_dd(Ny,gridy,rl2,ygi,YI-grid_offset[YY],
- ncpddc[YY],sh0[YY],sh1[YY],&dy0,&dy1,dcy2);
-#endif
- if (dy0 > dy1)
- {
- continue;
- }
- for (tx=-shp[XX]; tx<=shp[XX]; tx++)
- {
- XI = cgcm[icg][XX]+tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
- /* Calculate range of cells in X direction that have the shift tx */
- if (bTriclinicX)
- {
- xgi = (int)(Nx + (XI - grid_offset[XX])*grid_x) - Nx;
- }
- else
- {
- xgi = cell_x + tx*Nx;
- }
-#ifndef FAST_DD_NS
- get_dx(Nx,gridx,rl2,xgi*Nx,XI,&dx0,&dx1,dcx2);
-#else
- get_dx_dd(Nx,gridx,rl2,xgi,XI-grid_offset[XX],
- ncpddc[XX],sh0[XX],sh1[XX],&dx0,&dx1,dcx2);
-#endif
- if (dx0 > dx1)
- {
- continue;
- }
- /* Get shift vector */
- shift=XYZ2IS(tx,ty,tz);
-#ifdef NS5DB
- range_check(shift,0,SHIFTS);
-#endif
- for(nn=0; (nn<ngid); nn++)
- {
- nsr[nn] = 0;
- nlr_ljc[nn] = 0;
- nlr_one[nn] = 0;
- }
-#ifdef NS5DB
- fprintf(log,"shift: %2d, dx0,1: %2d,%2d, dy0,1: %2d,%2d, dz0,1: %2d,%2d\n",
- shift,dx0,dx1,dy0,dy1,dz0,dz1);
- fprintf(log,"cgcm: %8.3f %8.3f %8.3f\n",cgcm[icg][XX],
- cgcm[icg][YY],cgcm[icg][ZZ]);
- fprintf(log,"xi: %8.3f %8.3f %8.3f\n",XI,YI,ZI);
-#endif
- for (dx=dx0; (dx<=dx1); dx++)
- {
- tmp1 = rl2 - dcx2[dx];
- for (dy=dy0; (dy<=dy1); dy++)
- {
- tmp2 = tmp1 - dcy2[dy];
- if (tmp2 > 0)
- {
- for (dz=dz0; (dz<=dz1); dz++) {
- if (tmp2 > dcz2[dz]) {
- /* Find grid-cell cj in which possible neighbours are */
- cj = xyz2ci(Ny,Nz,dx,dy,dz);
-
- /* Check out how many cgs (nrj) there in this cell */
- nrj = gridnra[cj];
-
- /* Find the offset in the cg list */
- cgj0 = gridind[cj];
-
- /* Check if all j's are out of range so we
- * can skip the whole cell.
- * Should save some time, especially with DD.
- */
- if (nrj == 0 ||
- (grida[cgj0] >= max_jcg &&
- (grida[cgj0] >= jcg1 || grida[cgj0+nrj-1] < jcg0)))
- {
- continue;
- }
-
- /* Loop over cgs */
- for (j=0; (j<nrj); j++)
- {
- jjcg = grida[cgj0+j];
-
- /* check whether this guy is in range! */
- if ((jjcg >= jcg0 && jjcg < jcg1) ||
- (jjcg < max_jcg))
- {
- r2=calc_dx2(XI,YI,ZI,cgcm[jjcg]);
- if (r2 < rl2) {
- /* jgid = gid[cgsatoms[cgsindex[jjcg]]]; */
- jgid = GET_CGINFO_GID(cginfo[jjcg]);
- /* check energy group exclusions */
- if (!(i_egp_flags[jgid] & EGP_EXCL))
- {
- if (r2 < rs2)
- {
- if (nsr[jgid] >= MAX_CG)
- {
- put_in_list(bHaveVdW,ngid,md,icg,jgid,
- nsr[jgid],nl_sr[jgid],
- cgs->index,/* cgsatoms, */ bexcl,
- shift,fr,FALSE,TRUE,TRUE);
- nsr[jgid]=0;
- }
- nl_sr[jgid][nsr[jgid]++]=jjcg;
- }
- else if (r2 < rm2)
- {
- if (nlr_ljc[jgid] >= MAX_CG)
- {
- do_longrange(cr,top,fr,ngid,md,icg,jgid,
- nlr_ljc[jgid],
- nl_lr_ljc[jgid],bexcl,shift,x,
- box_size,nrnb,
- lambda,dvdlambda,
- grppener,
- TRUE,TRUE,FALSE,
- put_in_list,
- bHaveVdW,
- bDoForces,f);
- nlr_ljc[jgid]=0;
- }
- nl_lr_ljc[jgid][nlr_ljc[jgid]++]=jjcg;
- }
- else
- {
- if (nlr_one[jgid] >= MAX_CG) {
- do_longrange(cr,top,fr,ngid,md,icg,jgid,
- nlr_one[jgid],
- nl_lr_one[jgid],bexcl,shift,x,
- box_size,nrnb,
- lambda,dvdlambda,
- grppener,
- rvdw_lt_rcoul,rcoul_lt_rvdw,FALSE,
- put_in_list,
- bHaveVdW,
- bDoForces,f);
- nlr_one[jgid]=0;
- }
- nl_lr_one[jgid][nlr_one[jgid]++]=jjcg;
- }
- }
- }
- nns++;
- }
- }
- }
- }
- }
- }
- }
- /* CHECK whether there is anything left in the buffers */
- for(nn=0; (nn<ngid); nn++)
- {
- if (nsr[nn] > 0)
- {
- put_in_list(bHaveVdW,ngid,md,icg,nn,nsr[nn],nl_sr[nn],
- cgs->index, /* cgsatoms, */ bexcl,
- shift,fr,FALSE,TRUE,TRUE);
- }
-
- if (nlr_ljc[nn] > 0)
- {
- do_longrange(cr,top,fr,ngid,md,icg,nn,nlr_ljc[nn],
- nl_lr_ljc[nn],bexcl,shift,x,box_size,nrnb,
- lambda,dvdlambda,grppener,TRUE,TRUE,FALSE,
- put_in_list,bHaveVdW,bDoForces,f);
- }
-
- if (nlr_one[nn] > 0)
- {
- do_longrange(cr,top,fr,ngid,md,icg,nn,nlr_one[nn],
- nl_lr_one[nn],bexcl,shift,x,box_size,nrnb,
- lambda,dvdlambda,grppener,
- rvdw_lt_rcoul,rcoul_lt_rvdw,FALSE,
- put_in_list,bHaveVdW,bDoForces,f);
- }
- }
- }
- }
- }
- /* setexcl(nri,i_atoms,&top->atoms.excl,FALSE,bexcl); */
- setexcl(cgs->index[icg],cgs->index[icg+1],&top->excls,FALSE,bexcl);
- }
- /* Perform any left over force calculations */
- for (nn=0; (nn<ngid); nn++)
- {
- if (rm2 > rs2)
- {
- do_longrange(cr,top,fr,0,md,icg,nn,nlr_ljc[nn],
- nl_lr_ljc[nn],bexcl,shift,x,box_size,nrnb,
- lambda,dvdlambda,grppener,
- TRUE,TRUE,TRUE,put_in_list,bHaveVdW,bDoForces,f);
- }
- if (rl2 > rm2) {
- do_longrange(cr,top,fr,0,md,icg,nn,nlr_one[nn],
- nl_lr_one[nn],bexcl,shift,x,box_size,nrnb,
- lambda,dvdlambda,grppener,
- rvdw_lt_rcoul,rcoul_lt_rvdw,
- TRUE,put_in_list,bHaveVdW,bDoForces,f);
- }
- }
- debug_gmx();
-
- /* Close off short range neighbourlists */
- close_neighbor_list(fr,FALSE,-1,-1,bMakeQMMMnblist);
-
- return nns;
-}
-
-void ns_realloc_natoms(gmx_ns_t *ns,int natoms)
-{
- int i;
-
- if (natoms > ns->nra_alloc)
- {
- ns->nra_alloc = over_alloc_dd(natoms);
- srenew(ns->bexcl,ns->nra_alloc);
- for(i=0; i<ns->nra_alloc; i++)
- {
- ns->bexcl[i] = 0;
- }
- }
-}
-
-void init_ns(FILE *fplog,const t_commrec *cr,
- gmx_ns_t *ns,t_forcerec *fr,
- const gmx_mtop_t *mtop,
- matrix box)
-{
- int mt,icg,nr_in_cg,maxcg,i,j,jcg,ngid,ncg;
- t_block *cgs;
- char *ptr;
-
- /* Compute largest charge groups size (# atoms) */
- nr_in_cg=1;
- for(mt=0; mt<mtop->nmoltype; mt++) {
- cgs = &mtop->moltype[mt].cgs;
- for (icg=0; (icg < cgs->nr); icg++)
- {
- nr_in_cg=max(nr_in_cg,(int)(cgs->index[icg+1]-cgs->index[icg]));
- }
- }
-
- /* Verify whether largest charge group is <= max cg.
- * This is determined by the type of the local exclusion type
- * Exclusions are stored in bits. (If the type is not large
- * enough, enlarge it, unsigned char -> unsigned short -> unsigned long)
- */
- maxcg = sizeof(t_excl)*8;
- if (nr_in_cg > maxcg)
- {
- gmx_fatal(FARGS,"Max #atoms in a charge group: %d > %d\n",
- nr_in_cg,maxcg);
- }
-
- ngid = mtop->groups.grps[egcENER].nr;
- snew(ns->bExcludeAlleg,ngid);
- for(i=0; i<ngid; i++) {
- ns->bExcludeAlleg[i] = TRUE;
- for(j=0; j<ngid; j++)
- {
- if (!(fr->egp_flags[i*ngid+j] & EGP_EXCL))
- {
- ns->bExcludeAlleg[i] = FALSE;
- }
- }
- }
-
- if (fr->bGrid) {
- /* Grid search */
- ns->grid = init_grid(fplog,fr);
- init_nsgrid_lists(fr,ngid,ns);
- }
- else
- {
- /* Simple search */
- snew(ns->ns_buf,ngid);
- for(i=0; (i<ngid); i++)
- {
- snew(ns->ns_buf[i],SHIFTS);
- }
- ncg = ncg_mtop(mtop);
- snew(ns->simple_aaj,2*ncg);
- for(jcg=0; (jcg<ncg); jcg++)
- {
- ns->simple_aaj[jcg] = jcg;
- ns->simple_aaj[jcg+ncg] = jcg;
- }
- }
-
- /* Create array that determines whether or not atoms have VdW */
- snew(ns->bHaveVdW,fr->ntype);
- for(i=0; (i<fr->ntype); i++)
- {
- for(j=0; (j<fr->ntype); j++)
- {
- ns->bHaveVdW[i] = (ns->bHaveVdW[i] ||
- (fr->bBHAM ?
- ((BHAMA(fr->nbfp,fr->ntype,i,j) != 0) ||
- (BHAMB(fr->nbfp,fr->ntype,i,j) != 0) ||
- (BHAMC(fr->nbfp,fr->ntype,i,j) != 0)) :
- ((C6(fr->nbfp,fr->ntype,i,j) != 0) ||
- (C12(fr->nbfp,fr->ntype,i,j) != 0))));
- }
- }
- if (debug)
- pr_bvec(debug,0,"bHaveVdW",ns->bHaveVdW,fr->ntype,TRUE);
-
- ns->nra_alloc = 0;
- ns->bexcl = NULL;
- if (!DOMAINDECOMP(cr))
- {
- /* This could be reduced with particle decomposition */
- ns_realloc_natoms(ns,mtop->natoms);
- }
-
- ns->nblist_initialized=FALSE;
-
- /* nbr list debug dump */
- {
- char *ptr=getenv("GMX_DUMP_NL");
- if (ptr)
- {
- ns->dump_nl=strtol(ptr,NULL,10);
- if (fplog)
- {
- fprintf(fplog, "GMX_DUMP_NL = %d", ns->dump_nl);
- }
- }
- else
- {
- ns->dump_nl=0;
- }
- }
-}
-
-
-int search_neighbours(FILE *log,t_forcerec *fr,
- rvec x[],matrix box,
- gmx_localtop_t *top,
- gmx_groups_t *groups,
- t_commrec *cr,
- t_nrnb *nrnb,t_mdatoms *md,
- real lambda,real *dvdlambda,
- gmx_grppairener_t *grppener,
- gmx_bool bFillGrid,
- gmx_bool bDoLongRange,
- gmx_bool bDoForces,rvec *f)
-{
- t_block *cgs=&(top->cgs);
- rvec box_size,grid_x0,grid_x1;
- int i,j,m,ngid;
- real min_size,grid_dens;
- int nsearch;
- gmx_bool bGrid;
- char *ptr;
- gmx_bool *i_egp_flags;
- int cg_start,cg_end,start,end;
- gmx_ns_t *ns;
- t_grid *grid;
- gmx_domdec_zones_t *dd_zones;
- put_in_list_t *put_in_list;
-
- ns = &fr->ns;
-
- /* Set some local variables */
- bGrid = fr->bGrid;
- ngid = groups->grps[egcENER].nr;
-
- for(m=0; (m<DIM); m++)
- {
- box_size[m] = box[m][m];
- }
-
- if (fr->ePBC != epbcNONE)
- {
- if (sqr(fr->rlistlong) >= max_cutoff2(fr->ePBC,box))
- {
- gmx_fatal(FARGS,"One of the box vectors has become shorter than twice the cut-off length or box_yy-|box_zy| or box_zz has become smaller than the cut-off.");
- }
- if (!bGrid)
- {
- min_size = min(box_size[XX],min(box_size[YY],box_size[ZZ]));
- if (2*fr->rlistlong >= min_size)
- gmx_fatal(FARGS,"One of the box diagonal elements has become smaller than twice the cut-off length.");
- }
- }
-
- if (DOMAINDECOMP(cr))
- {
- ns_realloc_natoms(ns,cgs->index[cgs->nr]);
- }
- debug_gmx();
-
- /* Reset the neighbourlists */
- reset_neighbor_list(fr,FALSE,-1,-1);
-
- if (bGrid && bFillGrid)
- {
-
- grid = ns->grid;
- if (DOMAINDECOMP(cr))
- {
- dd_zones = domdec_zones(cr->dd);
- }
- else
- {
- dd_zones = NULL;
-
- get_nsgrid_boundaries(grid,NULL,box,NULL,NULL,NULL,
- cgs->nr,fr->cg_cm,grid_x0,grid_x1,&grid_dens);
-
- grid_first(log,grid,NULL,NULL,fr->ePBC,box,grid_x0,grid_x1,
- fr->rlistlong,grid_dens);
- }
- debug_gmx();
-
- /* Don't know why this all is... (DvdS 3/99) */
-#ifndef SEGV
- start = 0;
- end = cgs->nr;
-#else
- start = fr->cg0;
- end = (cgs->nr+1)/2;
-#endif
-
- if (DOMAINDECOMP(cr))
- {
- end = cgs->nr;
- fill_grid(log,dd_zones,grid,end,-1,end,fr->cg_cm);
- grid->icg0 = 0;
- grid->icg1 = dd_zones->izone[dd_zones->nizone-1].cg1;
- }
- else
- {
- fill_grid(log,NULL,grid,cgs->nr,fr->cg0,fr->hcg,fr->cg_cm);
- grid->icg0 = fr->cg0;
- grid->icg1 = fr->hcg;
- debug_gmx();
-
- if (PARTDECOMP(cr))
- mv_grid(cr,grid);
- debug_gmx();
- }
-
- calc_elemnr(log,grid,start,end,cgs->nr);
- calc_ptrs(grid);
- grid_last(log,grid,start,end,cgs->nr);
-
- if (gmx_debug_at)
- {
- check_grid(debug,grid);
- print_grid(debug,grid);
- }
- }
- else if (fr->n_tpi)
- {
- /* Set the grid cell index for the test particle only.
- * The cell to cg index is not corrected, but that does not matter.
- */
- fill_grid(log,NULL,ns->grid,fr->hcg,fr->hcg-1,fr->hcg,fr->cg_cm);
- }
- debug_gmx();
-
- if (!fr->ns.bCGlist)
- {
- put_in_list = put_in_list_at;
- }
- else
- {
- put_in_list = put_in_list_cg;
- }
-
- /* Do the core! */
- if (bGrid)
- {
- grid = ns->grid;
- nsearch = nsgrid_core(log,cr,fr,box,box_size,ngid,top,
- grid,x,ns->bexcl,ns->bExcludeAlleg,
- nrnb,md,lambda,dvdlambda,grppener,
- put_in_list,ns->bHaveVdW,
- bDoLongRange,bDoForces,f,
- FALSE);
-
- /* neighbour searching withouth QMMM! QM atoms have zero charge in
- * the classical calculation. The charge-charge interaction
- * between QM and MM atoms is handled in the QMMM core calculation
- * (see QMMM.c). The VDW however, we'd like to compute classically
- * and the QM MM atom pairs have just been put in the
- * corresponding neighbourlists. in case of QMMM we still need to
- * fill a special QMMM neighbourlist that contains all neighbours
- * of the QM atoms. If bQMMM is true, this list will now be made:
- */
- if (fr->bQMMM && fr->qr->QMMMscheme!=eQMMMschemeoniom)
- {
- nsearch += nsgrid_core(log,cr,fr,box,box_size,ngid,top,
- grid,x,ns->bexcl,ns->bExcludeAlleg,
- nrnb,md,lambda,dvdlambda,grppener,
- put_in_list_qmmm,ns->bHaveVdW,
- bDoLongRange,bDoForces,f,
- TRUE);
- }
- }
- else
- {
- nsearch = ns_simple_core(fr,top,md,box,box_size,
- ns->bexcl,ns->simple_aaj,
- ngid,ns->ns_buf,put_in_list,ns->bHaveVdW);
- }
- debug_gmx();
-
-#ifdef DEBUG
- pr_nsblock(log);
-#endif
-
- inc_nrnb(nrnb,eNR_NS,nsearch);
- /* inc_nrnb(nrnb,eNR_LR,fr->nlr); */
-
- return nsearch;
-}
-
-int natoms_beyond_ns_buffer(t_inputrec *ir,t_forcerec *fr,t_block *cgs,
- matrix scale_tot,rvec *x)
-{
- int cg0,cg1,cg,a0,a1,a,i,j;
- real rint,hbuf2,scale;
- rvec *cg_cm,cgsc;
- gmx_bool bIsotropic;
- int nBeyond;
-
- nBeyond = 0;
-
- rint = max(ir->rcoulomb,ir->rvdw);
- if (ir->rlist < rint)
- {
- gmx_fatal(FARGS,"The neighbor search buffer has negative size: %f nm",
- ir->rlist - rint);
- }
- cg_cm = fr->cg_cm;
-
- cg0 = fr->cg0;
- cg1 = fr->hcg;
-
- if (!EI_DYNAMICS(ir->eI) || !DYNAMIC_BOX(*ir))
- {
- hbuf2 = sqr(0.5*(ir->rlist - rint));
- for(cg=cg0; cg<cg1; cg++)
- {
- a0 = cgs->index[cg];
- a1 = cgs->index[cg+1];
- for(a=a0; a<a1; a++)
- {
- if (distance2(cg_cm[cg],x[a]) > hbuf2)
- {
- nBeyond++;
- }
- }
- }
- }
- else
- {
- bIsotropic = TRUE;
- scale = scale_tot[0][0];
- for(i=1; i<DIM; i++)
- {
- /* With anisotropic scaling, the original spherical ns volumes become
- * ellipsoids. To avoid costly transformations we use the minimum
- * eigenvalue of the scaling matrix for determining the buffer size.
- * Since the lower half is 0, the eigenvalues are the diagonal elements.
- */
- scale = min(scale,scale_tot[i][i]);
- if (scale_tot[i][i] != scale_tot[i-1][i-1])
- {
- bIsotropic = FALSE;
- }
- for(j=0; j<i; j++)
- {
- if (scale_tot[i][j] != 0)
- {
- bIsotropic = FALSE;
- }
- }
- }
- hbuf2 = sqr(0.5*(scale*ir->rlist - rint));
- if (bIsotropic)
- {
- for(cg=cg0; cg<cg1; cg++)
- {
- svmul(scale,cg_cm[cg],cgsc);
- a0 = cgs->index[cg];
- a1 = cgs->index[cg+1];
- for(a=a0; a<a1; a++)
- {
- if (distance2(cgsc,x[a]) > hbuf2)
- {
- nBeyond++;
- }
- }
- }
- }
- else
- {
- /* Anistropic scaling */
- for(cg=cg0; cg<cg1; cg++)
- {
- /* Since scale_tot contains the transpose of the scaling matrix,
- * we need to multiply with the transpose.
- */
- tmvmul_ur0(scale_tot,cg_cm[cg],cgsc);
- a0 = cgs->index[cg];
- a1 = cgs->index[cg+1];
- for(a=a0; a<a1; a++)
- {
- if (distance2(cgsc,x[a]) > hbuf2)
- {
- nBeyond++;
- }
- }
- }
- }
- }
-
- return nBeyond;
-}
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROwing Monsters And Cloning Shrimps
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef GMX_CRAY_XT3
-#include<catamount/dclock.h>
-#endif
-
-
-#include <stdio.h>
-#include <time.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#include <math.h>
-#include "typedefs.h"
-#include "string2.h"
-#include "gmxfio.h"
-#include "smalloc.h"
-#include "names.h"
-#include "confio.h"
-#include "mvdata.h"
-#include "txtdump.h"
-#include "pbc.h"
-#include "chargegroup.h"
-#include "vec.h"
-#include "time.h"
-#include "nrnb.h"
-#include "mshift.h"
-#include "mdrun.h"
-#include "update.h"
-#include "physics.h"
-#include "main.h"
-#include "mdatoms.h"
-#include "force.h"
-#include "bondf.h"
-#include "pme.h"
-#include "pppm.h"
-#include "disre.h"
-#include "orires.h"
-#include "network.h"
-#include "calcmu.h"
-#include "constr.h"
-#include "xvgr.h"
-#include "trnio.h"
-#include "xtcio.h"
-#include "copyrite.h"
-
-#include "mpelogging.h"
-#include "domdec.h"
-#include "partdec.h"
-#include "gmx_wallcycle.h"
-#include "genborn.h"
-
-#ifdef GMX_LIB_MPI
-#include <mpi.h>
-#endif
-#ifdef GMX_THREADS
-#include "tmpi.h"
-#endif
-
-#include "qmmm.h"
-
-#if 0
-typedef struct gmx_timeprint {
-
-} t_gmx_timeprint;
-#endif
-
-/* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
-char *
-gmx_ctime_r(const time_t *clock,char *buf, int n);
-
-
-double
-gmx_gettime()
-{
-#ifdef HAVE_GETTIMEOFDAY
- struct timeval t;
- struct timezone tz = { 0,0 };
- double seconds;
-
- gettimeofday(&t,&tz);
-
- seconds = (double) t.tv_sec + 1e-6*(double)t.tv_usec;
-
- return seconds;
-#else
- double seconds;
-
- seconds = time(NULL);
-
- return seconds;
-#endif
-}
-
-
-#define difftime(end,start) ((double)(end)-(double)(start))
-
-void print_time(FILE *out,gmx_runtime_t *runtime,gmx_large_int_t step,
- t_inputrec *ir, t_commrec *cr)
-{
- time_t finish;
- char timebuf[STRLEN];
- double dt;
- char buf[48];
-
-#ifndef GMX_THREADS
- if (!PAR(cr))
-#endif
- {
- fprintf(out,"\r");
- }
- fprintf(out,"step %s",gmx_step_str(step,buf));
- if ((step >= ir->nstlist))
- {
- if ((ir->nstlist == 0) || ((step % ir->nstlist) == 0))
- {
- /* We have done a full cycle let's update time_per_step */
- runtime->last = gmx_gettime();
- dt = difftime(runtime->last,runtime->real);
- runtime->time_per_step = dt/(step - ir->init_step + 1);
- }
- dt = (ir->nsteps + ir->init_step - step)*runtime->time_per_step;
-
- if (ir->nsteps >= 0)
- {
- if (dt >= 300)
- {
- finish = (time_t) (runtime->last + dt);
- gmx_ctime_r(&finish,timebuf,STRLEN);
- sprintf(buf,"%s",timebuf);
- buf[strlen(buf)-1]='\0';
- fprintf(out,", will finish %s",buf);
- }
- else
- fprintf(out,", remaining runtime: %5d s ",(int)dt);
- }
- else
- {
- fprintf(out," performance: %.1f ns/day ",
- ir->delta_t/1000*24*60*60/runtime->time_per_step);
- }
- }
-#ifndef GMX_THREADS
- if (PAR(cr))
- {
- fprintf(out,"\n");
- }
-#endif
-
- fflush(out);
-}
-
-#ifdef NO_CLOCK
-#define clock() -1
-#endif
-
-static double set_proctime(gmx_runtime_t *runtime)
-{
- double diff;
-#ifdef GMX_CRAY_XT3
- double prev;
-
- prev = runtime->proc;
- runtime->proc = dclock();
-
- diff = runtime->proc - prev;
-#else
- clock_t prev;
-
- prev = runtime->proc;
- runtime->proc = clock();
-
- diff = (double)(runtime->proc - prev)/(double)CLOCKS_PER_SEC;
-#endif
- if (diff < 0)
- {
- /* The counter has probably looped, ignore this data */
- diff = 0;
- }
-
- return diff;
-}
-
-void runtime_start(gmx_runtime_t *runtime)
-{
- runtime->real = gmx_gettime();
- runtime->proc = 0;
- set_proctime(runtime);
- runtime->realtime = 0;
- runtime->proctime = 0;
- runtime->last = 0;
- runtime->time_per_step = 0;
-}
-
-void runtime_end(gmx_runtime_t *runtime)
-{
- double now;
-
- now = gmx_gettime();
-
- runtime->proctime += set_proctime(runtime);
- runtime->realtime = now - runtime->real;
- runtime->real = now;
-}
-
-void runtime_upd_proc(gmx_runtime_t *runtime)
-{
- runtime->proctime += set_proctime(runtime);
-}
-
-void print_date_and_time(FILE *fplog,int nodeid,const char *title,
- const gmx_runtime_t *runtime)
-{
- int i;
- char timebuf[STRLEN];
- char time_string[STRLEN];
- time_t tmptime;
-
- if (fplog)
- {
- if (runtime != NULL)
- {
- tmptime = (time_t) runtime->real;
- gmx_ctime_r(&tmptime,timebuf,STRLEN);
- }
- else
- {
- tmptime = (time_t) gmx_gettime();
- gmx_ctime_r(&tmptime,timebuf,STRLEN);
- }
- for(i=0; timebuf[i]>=' '; i++)
- {
- time_string[i]=timebuf[i];
- }
- time_string[i]='\0';
-
- fprintf(fplog,"%s on node %d %s\n",title,nodeid,time_string);
- }
-}
-
-static void sum_forces(int start,int end,rvec f[],rvec flr[])
-{
- int i;
-
- if (gmx_debug_at) {
- pr_rvecs(debug,0,"fsr",f+start,end-start);
- pr_rvecs(debug,0,"flr",flr+start,end-start);
- }
- for(i=start; (i<end); i++)
- rvec_inc(f[i],flr[i]);
-}
-
-/*
- * calc_f_el calculates forces due to an electric field.
- *
- * force is kJ mol^-1 nm^-1 = e * kJ mol^-1 nm^-1 / e
- *
- * Et[] contains the parameters for the time dependent
- * part of the field (not yet used).
- * Ex[] contains the parameters for
- * the spatial dependent part of the field. You can have cool periodic
- * fields in principle, but only a constant field is supported
- * now.
- * The function should return the energy due to the electric field
- * (if any) but for now returns 0.
- *
- * WARNING:
- * There can be problems with the virial.
- * Since the field is not self-consistent this is unavoidable.
- * For neutral molecules the virial is correct within this approximation.
- * For neutral systems with many charged molecules the error is small.
- * But for systems with a net charge or a few charged molecules
- * the error can be significant when the field is high.
- * Solution: implement a self-consitent electric field into PME.
- */
-static void calc_f_el(FILE *fp,int start,int homenr,
- real charge[],rvec x[],rvec f[],
- t_cosines Ex[],t_cosines Et[],double t)
-{
- rvec Ext;
- real t0;
- int i,m;
-
- for(m=0; (m<DIM); m++)
- {
- if (Et[m].n > 0)
- {
- if (Et[m].n == 3)
- {
- t0 = Et[m].a[1];
- Ext[m] = cos(Et[m].a[0]*(t-t0))*exp(-sqr(t-t0)/(2.0*sqr(Et[m].a[2])));
- }
- else
- {
- Ext[m] = cos(Et[m].a[0]*t);
- }
- }
- else
- {
- Ext[m] = 1.0;
- }
- if (Ex[m].n > 0)
- {
- /* Convert the field strength from V/nm to MD-units */
- Ext[m] *= Ex[m].a[0]*FIELDFAC;
- for(i=start; (i<start+homenr); i++)
- f[i][m] += charge[i]*Ext[m];
- }
- else
- {
- Ext[m] = 0;
- }
- }
- if (fp != NULL)
- {
- fprintf(fp,"%10g %10g %10g %10g #FIELD\n",t,
- Ext[XX]/FIELDFAC,Ext[YY]/FIELDFAC,Ext[ZZ]/FIELDFAC);
- }
-}
-
-static void calc_virial(FILE *fplog,int start,int homenr,rvec x[],rvec f[],
- tensor vir_part,t_graph *graph,matrix box,
- t_nrnb *nrnb,const t_forcerec *fr,int ePBC)
-{
- int i,j;
- tensor virtest;
-
- /* The short-range virial from surrounding boxes */
- clear_mat(vir_part);
- calc_vir(fplog,SHIFTS,fr->shift_vec,fr->fshift,vir_part,ePBC==epbcSCREW,box);
- inc_nrnb(nrnb,eNR_VIRIAL,SHIFTS);
-
- /* Calculate partial virial, for local atoms only, based on short range.
- * Total virial is computed in global_stat, called from do_md
- */
- f_calc_vir(fplog,start,start+homenr,x,f,vir_part,graph,box);
- inc_nrnb(nrnb,eNR_VIRIAL,homenr);
-
- /* Add position restraint contribution */
- for(i=0; i<DIM; i++) {
- vir_part[i][i] += fr->vir_diag_posres[i];
- }
-
- /* Add wall contribution */
- for(i=0; i<DIM; i++) {
- vir_part[i][ZZ] += fr->vir_wall_z[i];
- }
-
- if (debug)
- pr_rvecs(debug,0,"vir_part",vir_part,DIM);
-}
-
-static void print_large_forces(FILE *fp,t_mdatoms *md,t_commrec *cr,
- gmx_large_int_t step,real pforce,rvec *x,rvec *f)
-{
- int i;
- real pf2,fn2;
- char buf[STEPSTRSIZE];
-
- pf2 = sqr(pforce);
- for(i=md->start; i<md->start+md->homenr; i++) {
- fn2 = norm2(f[i]);
- /* We also catch NAN, if the compiler does not optimize this away. */
- if (fn2 >= pf2 || fn2 != fn2) {
- fprintf(fp,"step %s atom %6d x %8.3f %8.3f %8.3f force %12.5e\n",
- gmx_step_str(step,buf),
- ddglatnr(cr->dd,i),x[i][XX],x[i][YY],x[i][ZZ],sqrt(fn2));
- }
- }
-}
-
-void do_force(FILE *fplog,t_commrec *cr,
- t_inputrec *inputrec,
- gmx_large_int_t step,t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_localtop_t *top,
- gmx_mtop_t *mtop,
- gmx_groups_t *groups,
- matrix box,rvec x[],history_t *hist,
- rvec f[],
- tensor vir_force,
- t_mdatoms *mdatoms,
- gmx_enerdata_t *enerd,t_fcdata *fcd,
- real lambda,t_graph *graph,
- t_forcerec *fr,gmx_vsite_t *vsite,rvec mu_tot,
- double t,FILE *field,gmx_edsam_t ed,
- gmx_bool bBornRadii,
- int flags)
-{
- int cg0,cg1,i,j;
- int start,homenr;
- double mu[2*DIM];
- gmx_bool bSepDVDL,bStateChanged,bNS,bFillGrid,bCalcCGCM,bBS;
- gmx_bool bDoLongRange,bDoForces,bSepLRF;
- matrix boxs;
- real e,v,dvdl;
- t_pbc pbc;
- float cycles_ppdpme,cycles_pme,cycles_seppme,cycles_force;
-
- start = mdatoms->start;
- homenr = mdatoms->homenr;
-
- bSepDVDL = (fr->bSepDVDL && do_per_step(step,inputrec->nstlog));
-
- clear_mat(vir_force);
-
- if (PARTDECOMP(cr))
- {
- pd_cg_range(cr,&cg0,&cg1);
- }
- else
- {
- cg0 = 0;
- if (DOMAINDECOMP(cr))
- {
- cg1 = cr->dd->ncg_tot;
- }
- else
- {
- cg1 = top->cgs.nr;
- }
- if (fr->n_tpi > 0)
- {
- cg1--;
- }
- }
-
- bStateChanged = (flags & GMX_FORCE_STATECHANGED);
- bNS = (flags & GMX_FORCE_NS) && (fr->bAllvsAll==FALSE);
- bFillGrid = (bNS && bStateChanged);
- bCalcCGCM = (bFillGrid && !DOMAINDECOMP(cr));
- bDoLongRange = (fr->bTwinRange && bNS && (flags & GMX_FORCE_DOLR));
- bDoForces = (flags & GMX_FORCE_FORCES);
- bSepLRF = (bDoLongRange && bDoForces && (flags & GMX_FORCE_SEPLRF));
-
- if (bStateChanged)
- {
- update_forcerec(fplog,fr,box);
-
- /* Calculate total (local) dipole moment in a temporary common array.
- * This makes it possible to sum them over nodes faster.
- */
- calc_mu(start,homenr,
- x,mdatoms->chargeA,mdatoms->chargeB,mdatoms->nChargePerturbed,
- mu,mu+DIM);
- }
-
- if (fr->ePBC != epbcNONE) {
- /* Compute shift vectors every step,
- * because of pressure coupling or box deformation!
- */
- if ((flags & GMX_FORCE_DYNAMICBOX) && bStateChanged)
- calc_shifts(box,fr->shift_vec);
-
- if (bCalcCGCM) {
- put_charge_groups_in_box(fplog,cg0,cg1,fr->ePBC,box,
- &(top->cgs),x,fr->cg_cm);
- inc_nrnb(nrnb,eNR_CGCM,homenr);
- inc_nrnb(nrnb,eNR_RESETX,cg1-cg0);
- }
- else if (EI_ENERGY_MINIMIZATION(inputrec->eI) && graph) {
- unshift_self(graph,box,x);
- }
- }
- else if (bCalcCGCM) {
- calc_cgcm(fplog,cg0,cg1,&(top->cgs),x,fr->cg_cm);
- inc_nrnb(nrnb,eNR_CGCM,homenr);
- }
-
- if (bCalcCGCM) {
- if (PAR(cr)) {
- move_cgcm(fplog,cr,fr->cg_cm);
- }
- if (gmx_debug_at)
- pr_rvecs(debug,0,"cgcm",fr->cg_cm,top->cgs.nr);
- }
-
-#ifdef GMX_MPI
- if (!(cr->duty & DUTY_PME)) {
- /* Send particle coordinates to the pme nodes.
- * Since this is only implemented for domain decomposition
- * and domain decomposition does not use the graph,
- * we do not need to worry about shifting.
- */
-
- wallcycle_start(wcycle,ewcPP_PMESENDX);
- GMX_MPE_LOG(ev_send_coordinates_start);
-
- bBS = (inputrec->nwall == 2);
- if (bBS) {
- copy_mat(box,boxs);
- svmul(inputrec->wall_ewald_zfac,boxs[ZZ],boxs[ZZ]);
- }
-
- gmx_pme_send_x(cr,bBS ? boxs : box,x,
- mdatoms->nChargePerturbed,lambda,
- ( flags & GMX_FORCE_VIRIAL),step);
-
- GMX_MPE_LOG(ev_send_coordinates_finish);
- wallcycle_stop(wcycle,ewcPP_PMESENDX);
- }
-#endif /* GMX_MPI */
-
- /* Communicate coordinates and sum dipole if necessary */
- if (PAR(cr))
- {
- wallcycle_start(wcycle,ewcMOVEX);
- if (DOMAINDECOMP(cr))
- {
- dd_move_x(cr->dd,box,x);
- }
- else
- {
- move_x(fplog,cr,GMX_LEFT,GMX_RIGHT,x,nrnb);
- }
- /* When we don't need the total dipole we sum it in global_stat */
- if (bStateChanged && NEED_MUTOT(*inputrec))
- {
- gmx_sumd(2*DIM,mu,cr);
- }
- wallcycle_stop(wcycle,ewcMOVEX);
- }
- if (bStateChanged)
- {
- for(i=0; i<2; i++)
- {
- for(j=0;j<DIM;j++)
- {
- fr->mu_tot[i][j] = mu[i*DIM + j];
- }
- }
- }
- if (fr->efep == efepNO)
- {
- copy_rvec(fr->mu_tot[0],mu_tot);
- }
- else
- {
- for(j=0; j<DIM; j++)
- {
- mu_tot[j] =
- (1.0 - lambda)*fr->mu_tot[0][j] + lambda*fr->mu_tot[1][j];
- }
- }
-
- /* Reset energies */
- reset_enerdata(&(inputrec->opts),fr,bNS,enerd,MASTER(cr));
- clear_rvecs(SHIFTS,fr->fshift);
-
- if (bNS)
- {
- wallcycle_start(wcycle,ewcNS);
-
- if (graph && bStateChanged)
- {
- /* Calculate intramolecular shift vectors to make molecules whole */
- mk_mshift(fplog,graph,fr->ePBC,box,x);
- }
-
- /* Reset long range forces if necessary */
- if (fr->bTwinRange)
- {
- /* Reset the (long-range) forces if necessary */
- clear_rvecs(fr->natoms_force_constr,bSepLRF ? fr->f_twin : f);
- }
-
- /* Do the actual neighbour searching and if twin range electrostatics
- * also do the calculation of long range forces and energies.
- */
- dvdl = 0;
- ns(fplog,fr,x,box,
- groups,&(inputrec->opts),top,mdatoms,
- cr,nrnb,lambda,&dvdl,&enerd->grpp,bFillGrid,
- bDoLongRange,bDoForces,bSepLRF ? fr->f_twin : f);
- if (bSepDVDL)
- {
- fprintf(fplog,sepdvdlformat,"LR non-bonded",0.0,dvdl);
- }
- enerd->dvdl_lin += dvdl;
-
- wallcycle_stop(wcycle,ewcNS);
- }
-
- if (inputrec->implicit_solvent && bNS)
- {
- make_gb_nblist(cr,inputrec->gb_algorithm,inputrec->rlist,
- x,box,fr,&top->idef,graph,fr->born);
- }
-
- if (DOMAINDECOMP(cr))
- {
- if (!(cr->duty & DUTY_PME))
- {
- wallcycle_start(wcycle,ewcPPDURINGPME);
- dd_force_flop_start(cr->dd,nrnb);
- }
- }
-
- /* Start the force cycle counter.
- * This counter is stopped in do_forcelow_level.
- * No parallel communication should occur while this counter is running,
- * since that will interfere with the dynamic load balancing.
- */
- wallcycle_start(wcycle,ewcFORCE);
-
- if (bDoForces)
- {
- /* Reset forces for which the virial is calculated separately:
- * PME/Ewald forces if necessary */
- if (fr->bF_NoVirSum)
- {
- if (flags & GMX_FORCE_VIRIAL)
- {
- fr->f_novirsum = fr->f_novirsum_alloc;
- GMX_BARRIER(cr->mpi_comm_mygroup);
- if (fr->bDomDec)
- {
- clear_rvecs(fr->f_novirsum_n,fr->f_novirsum);
- }
- else
- {
- clear_rvecs(homenr,fr->f_novirsum+start);
- }
- GMX_BARRIER(cr->mpi_comm_mygroup);
- }
- else
- {
- /* We are not calculating the pressure so we do not need
- * a separate array for forces that do not contribute
- * to the pressure.
- */
- fr->f_novirsum = f;
- }
- }
-
- if (bSepLRF)
- {
- /* Add the long range forces to the short range forces */
- for(i=0; i<fr->natoms_force_constr; i++)
- {
- copy_rvec(fr->f_twin[i],f[i]);
- }
- }
- else if (!(fr->bTwinRange && bNS))
- {
- /* Clear the short-range forces */
- clear_rvecs(fr->natoms_force_constr,f);
- }
-
- clear_rvec(fr->vir_diag_posres);
-
- GMX_BARRIER(cr->mpi_comm_mygroup);
- }
- if (inputrec->ePull == epullCONSTRAINT)
- {
- clear_pull_forces(inputrec->pull);
- }
-
- /* update QMMMrec, if necessary */
- if(fr->bQMMM)
- {
- update_QMMMrec(cr,fr,x,mdatoms,box,top);
- }
-
- if ((flags & GMX_FORCE_BONDED) && top->idef.il[F_POSRES].nr > 0)
- {
- /* Position restraints always require full pbc */
- set_pbc(&pbc,inputrec->ePBC,box);
- v = posres(top->idef.il[F_POSRES].nr,top->idef.il[F_POSRES].iatoms,
- top->idef.iparams_posres,
- (const rvec*)x,fr->f_novirsum,fr->vir_diag_posres,
- inputrec->ePBC==epbcNONE ? NULL : &pbc,lambda,&dvdl,
- fr->rc_scaling,fr->ePBC,fr->posres_com,fr->posres_comB);
- if (bSepDVDL)
- {
- fprintf(fplog,sepdvdlformat,
- interaction_function[F_POSRES].longname,v,dvdl);
- }
- enerd->term[F_POSRES] += v;
- /* This linear lambda dependence assumption is only correct
- * when only k depends on lambda,
- * not when the reference position depends on lambda.
- * grompp checks for this.
- */
- enerd->dvdl_lin += dvdl;
- inc_nrnb(nrnb,eNR_POSRES,top->idef.il[F_POSRES].nr/2);
- }
-
- /* Compute the bonded and non-bonded energies and optionally forces */
- do_force_lowlevel(fplog,step,fr,inputrec,&(top->idef),
- cr,nrnb,wcycle,mdatoms,&(inputrec->opts),
- x,hist,f,enerd,fcd,mtop,top,fr->born,
- &(top->atomtypes),bBornRadii,box,
- lambda,graph,&(top->excls),fr->mu_tot,
- flags,&cycles_pme);
-
- cycles_force = wallcycle_stop(wcycle,ewcFORCE);
- GMX_BARRIER(cr->mpi_comm_mygroup);
-
- if (ed)
- {
- do_flood(fplog,cr,x,f,ed,box,step);
- }
-
- if (DOMAINDECOMP(cr))
- {
- dd_force_flop_stop(cr->dd,nrnb);
- if (wcycle)
- {
- dd_cycles_add(cr->dd,cycles_force-cycles_pme,ddCyclF);
- }
- }
-
- if (bDoForces)
- {
- if (IR_ELEC_FIELD(*inputrec))
- {
- /* Compute forces due to electric field */
- calc_f_el(MASTER(cr) ? field : NULL,
- start,homenr,mdatoms->chargeA,x,fr->f_novirsum,
- inputrec->ex,inputrec->et,t);
- }
-
- /* Communicate the forces */
- if (PAR(cr))
- {
- wallcycle_start(wcycle,ewcMOVEF);
- if (DOMAINDECOMP(cr))
- {
- dd_move_f(cr->dd,f,fr->fshift);
- /* Do we need to communicate the separate force array
- * for terms that do not contribute to the single sum virial?
- * Position restraints and electric fields do not introduce
- * inter-cg forces, only full electrostatics methods do.
- * When we do not calculate the virial, fr->f_novirsum = f,
- * so we have already communicated these forces.
- */
- if (EEL_FULL(fr->eeltype) && cr->dd->n_intercg_excl &&
- (flags & GMX_FORCE_VIRIAL))
- {
- dd_move_f(cr->dd,fr->f_novirsum,NULL);
- }
- if (bSepLRF)
- {
- /* We should not update the shift forces here,
- * since f_twin is already included in f.
- */
- dd_move_f(cr->dd,fr->f_twin,NULL);
- }
- }
- else
- {
- pd_move_f(cr,f,nrnb);
- if (bSepLRF)
- {
- pd_move_f(cr,fr->f_twin,nrnb);
- }
- }
- wallcycle_stop(wcycle,ewcMOVEF);
- }
-
- /* If we have NoVirSum forces, but we do not calculate the virial,
- * we sum fr->f_novirum=f later.
- */
- if (vsite && !(fr->bF_NoVirSum && !(flags & GMX_FORCE_VIRIAL)))
- {
- wallcycle_start(wcycle,ewcVSITESPREAD);
- spread_vsite_f(fplog,vsite,x,f,fr->fshift,nrnb,
- &top->idef,fr->ePBC,fr->bMolPBC,graph,box,cr);
- wallcycle_stop(wcycle,ewcVSITESPREAD);
-
- if (bSepLRF)
- {
- wallcycle_start(wcycle,ewcVSITESPREAD);
- spread_vsite_f(fplog,vsite,x,fr->f_twin,NULL,
- nrnb,
- &top->idef,fr->ePBC,fr->bMolPBC,graph,box,cr);
- wallcycle_stop(wcycle,ewcVSITESPREAD);
- }
- }
-
- if (flags & GMX_FORCE_VIRIAL)
- {
- /* Calculation of the virial must be done after vsites! */
- calc_virial(fplog,mdatoms->start,mdatoms->homenr,x,f,
- vir_force,graph,box,nrnb,fr,inputrec->ePBC);
- }
- }
-
- if (inputrec->ePull == epullUMBRELLA || inputrec->ePull == epullCONST_F)
- {
- /* Calculate the center of mass forces, this requires communication,
- * which is why pull_potential is called close to other communication.
- * The virial contribution is calculated directly,
- * which is why we call pull_potential after calc_virial.
- */
- set_pbc(&pbc,inputrec->ePBC,box);
- dvdl = 0;
- enerd->term[F_COM_PULL] =
- pull_potential(inputrec->ePull,inputrec->pull,mdatoms,&pbc,
- cr,t,lambda,x,f,vir_force,&dvdl);
- if (bSepDVDL)
- {
- fprintf(fplog,sepdvdlformat,"Com pull",enerd->term[F_COM_PULL],dvdl);
- }
- enerd->dvdl_lin += dvdl;
- }
-
- if (PAR(cr) && !(cr->duty & DUTY_PME))
- {
- cycles_ppdpme = wallcycle_stop(wcycle,ewcPPDURINGPME);
- dd_cycles_add(cr->dd,cycles_ppdpme,ddCyclPPduringPME);
-
- /* In case of node-splitting, the PP nodes receive the long-range
- * forces, virial and energy from the PME nodes here.
- */
- wallcycle_start(wcycle,ewcPP_PMEWAITRECVF);
- dvdl = 0;
- gmx_pme_receive_f(cr,fr->f_novirsum,fr->vir_el_recip,&e,&dvdl,
- &cycles_seppme);
- if (bSepDVDL)
- {
- fprintf(fplog,sepdvdlformat,"PME mesh",e,dvdl);
- }
- enerd->term[F_COUL_RECIP] += e;
- enerd->dvdl_lin += dvdl;
- if (wcycle)
- {
- dd_cycles_add(cr->dd,cycles_seppme,ddCyclPME);
- }
- wallcycle_stop(wcycle,ewcPP_PMEWAITRECVF);
- }
-
- if (bDoForces && fr->bF_NoVirSum)
- {
- if (vsite)
- {
- /* Spread the mesh force on virtual sites to the other particles...
- * This is parallellized. MPI communication is performed
- * if the constructing atoms aren't local.
- */
- wallcycle_start(wcycle,ewcVSITESPREAD);
- spread_vsite_f(fplog,vsite,x,fr->f_novirsum,NULL,nrnb,
- &top->idef,fr->ePBC,fr->bMolPBC,graph,box,cr);
- wallcycle_stop(wcycle,ewcVSITESPREAD);
- }
- if (flags & GMX_FORCE_VIRIAL)
- {
- /* Now add the forces, this is local */
- if (fr->bDomDec)
- {
- sum_forces(0,fr->f_novirsum_n,f,fr->f_novirsum);
- }
- else
- {
- sum_forces(start,start+homenr,f,fr->f_novirsum);
- }
- if (EEL_FULL(fr->eeltype))
- {
- /* Add the mesh contribution to the virial */
- m_add(vir_force,fr->vir_el_recip,vir_force);
- }
- if (debug)
- {
- pr_rvecs(debug,0,"vir_force",vir_force,DIM);
- }
- }
- }
-
- /* Sum the potential energy terms from group contributions */
- sum_epot(&(inputrec->opts),enerd);
-
- if (fr->print_force >= 0 && bDoForces)
- {
- print_large_forces(stderr,mdatoms,cr,step,fr->print_force,x,f);
- }
-}
-
-void do_constrain_first(FILE *fplog,gmx_constr_t constr,
- t_inputrec *ir,t_mdatoms *md,
- t_state *state,rvec *f,
- t_graph *graph,t_commrec *cr,t_nrnb *nrnb,
- t_forcerec *fr, gmx_localtop_t *top, tensor shake_vir)
-{
- int i,m,start,end;
- gmx_large_int_t step;
- double mass,tmass,vcm[4];
- real dt=ir->delta_t;
- real dvdlambda;
- rvec *savex;
-
- snew(savex,state->natoms);
-
- start = md->start;
- end = md->homenr + start;
-
- if (debug)
- fprintf(debug,"vcm: start=%d, homenr=%d, end=%d\n",
- start,md->homenr,end);
- /* Do a first constrain to reset particles... */
- step = ir->init_step;
- if (fplog)
- {
- char buf[STEPSTRSIZE];
- fprintf(fplog,"\nConstraining the starting coordinates (step %s)\n",
- gmx_step_str(step,buf));
- }
- dvdlambda = 0;
-
- /* constrain the current position */
- constrain(NULL,TRUE,FALSE,constr,&(top->idef),
- ir,NULL,cr,step,0,md,
- state->x,state->x,NULL,
- state->box,state->lambda,&dvdlambda,
- NULL,NULL,nrnb,econqCoord,ir->epc==epcMTTK,state->veta,state->veta);
- if (EI_VV(ir->eI))
- {
- /* constrain the inital velocity, and save it */
- /* also may be useful if we need the ekin from the halfstep for velocity verlet */
- /* might not yet treat veta correctly */
- constrain(NULL,TRUE,FALSE,constr,&(top->idef),
- ir,NULL,cr,step,0,md,
- state->x,state->v,state->v,
- state->box,state->lambda,&dvdlambda,
- NULL,NULL,nrnb,econqVeloc,ir->epc==epcMTTK,state->veta,state->veta);
- }
- /* constrain the inital velocities at t-dt/2 */
- if (EI_STATE_VELOCITY(ir->eI) && ir->eI!=eiVV)
- {
- for(i=start; (i<end); i++)
- {
- for(m=0; (m<DIM); m++)
- {
- /* Reverse the velocity */
- state->v[i][m] = -state->v[i][m];
- /* Store the position at t-dt in buf */
- savex[i][m] = state->x[i][m] + dt*state->v[i][m];
- }
- }
- /* Shake the positions at t=-dt with the positions at t=0
- * as reference coordinates.
- */
- if (fplog)
- {
- char buf[STEPSTRSIZE];
- fprintf(fplog,"\nConstraining the coordinates at t0-dt (step %s)\n",
- gmx_step_str(step,buf));
- }
- dvdlambda = 0;
- constrain(NULL,TRUE,FALSE,constr,&(top->idef),
- ir,NULL,cr,step,-1,md,
- state->x,savex,NULL,
- state->box,state->lambda,&dvdlambda,
- state->v,NULL,nrnb,econqCoord,ir->epc==epcMTTK,state->veta,state->veta);
-
- for(i=start; i<end; i++) {
- for(m=0; m<DIM; m++) {
- /* Re-reverse the velocities */
- state->v[i][m] = -state->v[i][m];
- }
- }
- }
-
- for(m=0; (m<4); m++)
- vcm[m] = 0;
- for(i=start; i<end; i++) {
- mass = md->massT[i];
- for(m=0; m<DIM; m++) {
- vcm[m] += state->v[i][m]*mass;
- }
- vcm[3] += mass;
- }
-
- if (ir->nstcomm != 0 || debug) {
- /* Compute the global sum of vcm */
- if (debug)
- fprintf(debug,"vcm: %8.3f %8.3f %8.3f,"
- " total mass = %12.5e\n",vcm[XX],vcm[YY],vcm[ZZ],vcm[3]);
- if (PAR(cr))
- gmx_sumd(4,vcm,cr);
- tmass = vcm[3];
- for(m=0; (m<DIM); m++)
- vcm[m] /= tmass;
- if (debug)
- fprintf(debug,"vcm: %8.3f %8.3f %8.3f,"
- " total mass = %12.5e\n",vcm[XX],vcm[YY],vcm[ZZ],tmass);
- if (ir->nstcomm != 0) {
- /* Now we have the velocity of center of mass, let's remove it */
- for(i=start; (i<end); i++) {
- for(m=0; (m<DIM); m++)
- state->v[i][m] -= vcm[m];
- }
-
- }
- }
- sfree(savex);
-}
-
-void calc_enervirdiff(FILE *fplog,int eDispCorr,t_forcerec *fr)
-{
- double eners[2],virs[2],enersum,virsum,y0,f,g,h;
- double r0,r1,r,rc3,rc9,ea,eb,ec,pa,pb,pc,pd;
- double invscale,invscale2,invscale3;
- int ri0,ri1,ri,i,offstart,offset;
- real scale,*vdwtab;
-
- fr->enershiftsix = 0;
- fr->enershifttwelve = 0;
- fr->enerdiffsix = 0;
- fr->enerdifftwelve = 0;
- fr->virdiffsix = 0;
- fr->virdifftwelve = 0;
-
- if (eDispCorr != edispcNO) {
- for(i=0; i<2; i++) {
- eners[i] = 0;
- virs[i] = 0;
- }
- if ((fr->vdwtype == evdwSWITCH) || (fr->vdwtype == evdwSHIFT)) {
- if (fr->rvdw_switch == 0)
- gmx_fatal(FARGS,
- "With dispersion correction rvdw-switch can not be zero "
- "for vdw-type = %s",evdw_names[fr->vdwtype]);
-
- scale = fr->nblists[0].tab.scale;
- vdwtab = fr->nblists[0].vdwtab;
-
- /* Round the cut-offs to exact table values for precision */
- ri0 = floor(fr->rvdw_switch*scale);
- ri1 = ceil(fr->rvdw*scale);
- r0 = ri0/scale;
- r1 = ri1/scale;
- rc3 = r0*r0*r0;
- rc9 = rc3*rc3*rc3;
-
- if (fr->vdwtype == evdwSHIFT) {
- /* Determine the constant energy shift below rvdw_switch */
- fr->enershiftsix = (real)(-1.0/(rc3*rc3)) - vdwtab[8*ri0];
- fr->enershifttwelve = (real)( 1.0/(rc9*rc3)) - vdwtab[8*ri0 + 4];
- }
- /* Add the constant part from 0 to rvdw_switch.
- * This integration from 0 to rvdw_switch overcounts the number
- * of interactions by 1, as it also counts the self interaction.
- * We will correct for this later.
- */
- eners[0] += 4.0*M_PI*fr->enershiftsix*rc3/3.0;
- eners[1] += 4.0*M_PI*fr->enershifttwelve*rc3/3.0;
-
- invscale = 1.0/(scale);
- invscale2 = invscale*invscale;
- invscale3 = invscale*invscale2;
-
- /* following summation derived from cubic spline definition,
- Numerical Recipies in C, second edition, p. 113-116. Exact
- for the cubic spline. We first calculate the negative of
- the energy from rvdw to rvdw_switch, assuming that g(r)=1,
- and then add the more standard, abrupt cutoff correction to
- that result, yielding the long-range correction for a
- switched function. We perform both the pressure and energy
- loops at the same time for simplicity, as the computational
- cost is low. */
-
- for (i=0;i<2;i++) {
- enersum = 0.0; virsum = 0.0;
- if (i==0)
- offstart = 0;
- else
- offstart = 4;
- for (ri=ri0; ri<ri1; ri++) {
- r = ri*invscale;
- ea = invscale3;
- eb = 2.0*invscale2*r;
- ec = invscale*r*r;
-
- pa = invscale3;
- pb = 3.0*invscale2*r;
- pc = 3.0*invscale*r*r;
- pd = r*r*r;
-
- /* this "8" is from the packing in the vdwtab array - perhaps
- should be #define'ed? */
- offset = 8*ri + offstart;
- y0 = vdwtab[offset];
- f = vdwtab[offset+1];
- g = vdwtab[offset+2];
- h = vdwtab[offset+3];
-
- enersum += y0*(ea/3 + eb/2 + ec) + f*(ea/4 + eb/3 + ec/2)+
- g*(ea/5 + eb/4 + ec/3) + h*(ea/6 + eb/5 + ec/4);
- virsum += f*(pa/4 + pb/3 + pc/2 + pd) +
- 2*g*(pa/5 + pb/4 + pc/3 + pd/2) + 3*h*(pa/6 + pb/5 + pc/4 + pd/3);
-
- }
- enersum *= 4.0*M_PI;
- virsum *= 4.0*M_PI;
- eners[i] -= enersum;
- virs[i] -= virsum;
- }
-
- /* now add the correction for rvdw_switch to infinity */
- eners[0] += -4.0*M_PI/(3.0*rc3);
- eners[1] += 4.0*M_PI/(9.0*rc9);
- virs[0] += 8.0*M_PI/rc3;
- virs[1] += -16.0*M_PI/(3.0*rc9);
- }
- else if ((fr->vdwtype == evdwCUT) || (fr->vdwtype == evdwUSER)) {
- if (fr->vdwtype == evdwUSER && fplog)
- fprintf(fplog,
- "WARNING: using dispersion correction with user tables\n");
- rc3 = fr->rvdw*fr->rvdw*fr->rvdw;
- rc9 = rc3*rc3*rc3;
- eners[0] += -4.0*M_PI/(3.0*rc3);
- eners[1] += 4.0*M_PI/(9.0*rc9);
- virs[0] += 8.0*M_PI/rc3;
- virs[1] += -16.0*M_PI/(3.0*rc9);
- } else {
- gmx_fatal(FARGS,
- "Dispersion correction is not implemented for vdw-type = %s",
- evdw_names[fr->vdwtype]);
- }
- fr->enerdiffsix = eners[0];
- fr->enerdifftwelve = eners[1];
- /* The 0.5 is due to the Gromacs definition of the virial */
- fr->virdiffsix = 0.5*virs[0];
- fr->virdifftwelve = 0.5*virs[1];
- }
-}
-
-void calc_dispcorr(FILE *fplog,t_inputrec *ir,t_forcerec *fr,
- gmx_large_int_t step,int natoms,
- matrix box,real lambda,tensor pres,tensor virial,
- real *prescorr, real *enercorr, real *dvdlcorr)
-{
- gmx_bool bCorrAll,bCorrPres;
- real dvdlambda,invvol,dens,ninter,avcsix,avctwelve,enerdiff,svir=0,spres=0;
- int m;
-
- *prescorr = 0;
- *enercorr = 0;
- *dvdlcorr = 0;
-
- clear_mat(virial);
- clear_mat(pres);
-
- if (ir->eDispCorr != edispcNO) {
- bCorrAll = (ir->eDispCorr == edispcAllEner ||
- ir->eDispCorr == edispcAllEnerPres);
- bCorrPres = (ir->eDispCorr == edispcEnerPres ||
- ir->eDispCorr == edispcAllEnerPres);
-
- invvol = 1/det(box);
- if (fr->n_tpi)
- {
- /* Only correct for the interactions with the inserted molecule */
- dens = (natoms - fr->n_tpi)*invvol;
- ninter = fr->n_tpi;
- }
- else
- {
- dens = natoms*invvol;
- ninter = 0.5*natoms;
- }
-
- if (ir->efep == efepNO)
- {
- avcsix = fr->avcsix[0];
- avctwelve = fr->avctwelve[0];
- }
- else
- {
- avcsix = (1 - lambda)*fr->avcsix[0] + lambda*fr->avcsix[1];
- avctwelve = (1 - lambda)*fr->avctwelve[0] + lambda*fr->avctwelve[1];
- }
-
- enerdiff = ninter*(dens*fr->enerdiffsix - fr->enershiftsix);
- *enercorr += avcsix*enerdiff;
- dvdlambda = 0.0;
- if (ir->efep != efepNO)
- {
- dvdlambda += (fr->avcsix[1] - fr->avcsix[0])*enerdiff;
- }
- if (bCorrAll)
- {
- enerdiff = ninter*(dens*fr->enerdifftwelve - fr->enershifttwelve);
- *enercorr += avctwelve*enerdiff;
- if (fr->efep != efepNO)
- {
- dvdlambda += (fr->avctwelve[1] - fr->avctwelve[0])*enerdiff;
- }
- }
-
- if (bCorrPres)
- {
- svir = ninter*dens*avcsix*fr->virdiffsix/3.0;
- if (ir->eDispCorr == edispcAllEnerPres)
- {
- svir += ninter*dens*avctwelve*fr->virdifftwelve/3.0;
- }
- /* The factor 2 is because of the Gromacs virial definition */
- spres = -2.0*invvol*svir*PRESFAC;
-
- for(m=0; m<DIM; m++) {
- virial[m][m] += svir;
- pres[m][m] += spres;
- }
- *prescorr += spres;
- }
-
- /* Can't currently control when it prints, for now, just print when degugging */
- if (debug)
- {
- if (bCorrAll) {
- fprintf(debug,"Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
- avcsix,avctwelve);
- }
- if (bCorrPres)
- {
- fprintf(debug,
- "Long Range LJ corr.: Epot %10g, Pres: %10g, Vir: %10g\n",
- *enercorr,spres,svir);
- }
- else
- {
- fprintf(debug,"Long Range LJ corr.: Epot %10g\n",*enercorr);
- }
- }
-
- if (fr->bSepDVDL && do_per_step(step,ir->nstlog))
- {
- fprintf(fplog,sepdvdlformat,"Dispersion correction",
- *enercorr,dvdlambda);
- }
- if (fr->efep != efepNO)
- {
- *dvdlcorr += dvdlambda;
- }
- }
-}
-
-void do_pbc_first(FILE *fplog,matrix box,t_forcerec *fr,
- t_graph *graph,rvec x[])
-{
- if (fplog)
- fprintf(fplog,"Removing pbc first time\n");
- calc_shifts(box,fr->shift_vec);
- if (graph) {
- mk_mshift(fplog,graph,fr->ePBC,box,x);
- if (gmx_debug_at)
- p_graph(debug,"do_pbc_first 1",graph);
- shift_self(graph,box,x);
- /* By doing an extra mk_mshift the molecules that are broken
- * because they were e.g. imported from another software
- * will be made whole again. Such are the healing powers
- * of GROMACS.
- */
- mk_mshift(fplog,graph,fr->ePBC,box,x);
- if (gmx_debug_at)
- p_graph(debug,"do_pbc_first 2",graph);
- }
- if (fplog)
- fprintf(fplog,"Done rmpbc\n");
-}
-
-static void low_do_pbc_mtop(FILE *fplog,int ePBC,matrix box,
- gmx_mtop_t *mtop,rvec x[],
- gmx_bool bFirst)
-{
- t_graph *graph;
- int mb,as,mol;
- gmx_molblock_t *molb;
-
- if (bFirst && fplog)
- fprintf(fplog,"Removing pbc first time\n");
-
- snew(graph,1);
- as = 0;
- for(mb=0; mb<mtop->nmolblock; mb++) {
- molb = &mtop->molblock[mb];
- if (molb->natoms_mol == 1 ||
- (!bFirst && mtop->moltype[molb->type].cgs.nr == 1)) {
- /* Just one atom or charge group in the molecule, no PBC required */
- as += molb->nmol*molb->natoms_mol;
- } else {
- /* Pass NULL iso fplog to avoid graph prints for each molecule type */
- mk_graph_ilist(NULL,mtop->moltype[molb->type].ilist,
- 0,molb->natoms_mol,FALSE,FALSE,graph);
-
- for(mol=0; mol<molb->nmol; mol++) {
- mk_mshift(fplog,graph,ePBC,box,x+as);
-
- shift_self(graph,box,x+as);
- /* The molecule is whole now.
- * We don't need the second mk_mshift call as in do_pbc_first,
- * since we no longer need this graph.
- */
-
- as += molb->natoms_mol;
- }
- done_graph(graph);
- }
- }
- sfree(graph);
-}
-
-void do_pbc_first_mtop(FILE *fplog,int ePBC,matrix box,
- gmx_mtop_t *mtop,rvec x[])
-{
- low_do_pbc_mtop(fplog,ePBC,box,mtop,x,TRUE);
-}
-
-void do_pbc_mtop(FILE *fplog,int ePBC,matrix box,
- gmx_mtop_t *mtop,rvec x[])
-{
- low_do_pbc_mtop(fplog,ePBC,box,mtop,x,FALSE);
-}
-
-void finish_run(FILE *fplog,t_commrec *cr,const char *confout,
- t_inputrec *inputrec,
- t_nrnb nrnb[],gmx_wallcycle_t wcycle,
- gmx_runtime_t *runtime,
- gmx_bool bWriteStat)
-{
- int i,j;
- t_nrnb *nrnb_tot=NULL;
- real delta_t;
- double nbfs,mflop;
- double cycles[ewcNR];
-
- wallcycle_sum(cr,wcycle,cycles);
-
- if (cr->nnodes > 1) {
- if (SIMMASTER(cr))
- snew(nrnb_tot,1);
-#ifdef GMX_MPI
- MPI_Reduce(nrnb->n,nrnb_tot->n,eNRNB,MPI_DOUBLE,MPI_SUM,
- MASTERRANK(cr),cr->mpi_comm_mysim);
-#endif
- } else {
- nrnb_tot = nrnb;
- }
-
- if (SIMMASTER(cr)) {
- print_flop(fplog,nrnb_tot,&nbfs,&mflop);
- if (cr->nnodes > 1) {
- sfree(nrnb_tot);
- }
- }
-
- if ((cr->duty & DUTY_PP) && DOMAINDECOMP(cr)) {
- print_dd_statistics(cr,inputrec,fplog);
- }
-
-#ifdef GMX_MPI
- if (PARTDECOMP(cr))
- {
- if (MASTER(cr))
- {
- t_nrnb *nrnb_all;
- int s;
- MPI_Status stat;
-
- snew(nrnb_all,cr->nnodes);
- nrnb_all[0] = *nrnb;
- for(s=1; s<cr->nnodes; s++)
- {
- MPI_Recv(nrnb_all[s].n,eNRNB,MPI_DOUBLE,s,0,
- cr->mpi_comm_mysim,&stat);
- }
- pr_load(fplog,cr,nrnb_all);
- sfree(nrnb_all);
- }
- else
- {
- MPI_Send(nrnb->n,eNRNB,MPI_DOUBLE,MASTERRANK(cr),0,
- cr->mpi_comm_mysim);
- }
- }
-#endif
-
- if (SIMMASTER(cr)) {
- wallcycle_print(fplog,cr->nnodes,cr->npmenodes,runtime->realtime,
- wcycle,cycles);
-
- if (EI_DYNAMICS(inputrec->eI)) {
- delta_t = inputrec->delta_t;
- } else {
- delta_t = 0;
- }
-
- if (fplog) {
- print_perf(fplog,runtime->proctime,runtime->realtime,
- cr->nnodes-cr->npmenodes,
- runtime->nsteps_done,delta_t,nbfs,mflop);
- }
- if (bWriteStat) {
- print_perf(stderr,runtime->proctime,runtime->realtime,
- cr->nnodes-cr->npmenodes,
- runtime->nsteps_done,delta_t,nbfs,mflop);
- }
-
- /*
- runtime=inputrec->nsteps*inputrec->delta_t;
- if (bWriteStat) {
- if (cr->nnodes == 1)
- fprintf(stderr,"\n\n");
- print_perf(stderr,nodetime,realtime,runtime,&ntot,
- cr->nnodes-cr->npmenodes,FALSE);
- }
- wallcycle_print(fplog,cr->nnodes,cr->npmenodes,realtime,wcycle,cycles);
- print_perf(fplog,nodetime,realtime,runtime,&ntot,cr->nnodes-cr->npmenodes,
- TRUE);
- if (PARTDECOMP(cr))
- pr_load(fplog,cr,nrnb_all);
- if (cr->nnodes > 1)
- sfree(nrnb_all);
- */
- }
-}
-
-void init_md(FILE *fplog,
- t_commrec *cr,t_inputrec *ir,const output_env_t oenv,
- double *t,double *t0,
- real *lambda,double *lam0,
- t_nrnb *nrnb,gmx_mtop_t *mtop,
- gmx_update_t *upd,
- int nfile,const t_filenm fnm[],
- gmx_mdoutf_t **outf,t_mdebin **mdebin,
- tensor force_vir,tensor shake_vir,rvec mu_tot,
- gmx_bool *bSimAnn,t_vcm **vcm, t_state *state, unsigned long Flags)
-{
- int i,j,n;
- real tmpt,mod;
-
- /* Initial values */
- *t = *t0 = ir->init_t;
- if (ir->efep != efepNO)
- {
- *lam0 = ir->init_lambda;
- *lambda = *lam0 + ir->init_step*ir->delta_lambda;
- }
- else
- {
- *lambda = *lam0 = 0.0;
- }
-
- *bSimAnn=FALSE;
- for(i=0;i<ir->opts.ngtc;i++)
- {
- /* set bSimAnn if any group is being annealed */
- if(ir->opts.annealing[i]!=eannNO)
- {
- *bSimAnn = TRUE;
- }
- }
- if (*bSimAnn)
- {
- update_annealing_target_temp(&(ir->opts),ir->init_t);
- }
-
- if (upd)
- {
- *upd = init_update(fplog,ir);
- }
-
- if (vcm != NULL)
- {
- *vcm = init_vcm(fplog,&mtop->groups,ir);
- }
-
- if (EI_DYNAMICS(ir->eI) && !(Flags & MD_APPENDFILES))
- {
- if (ir->etc == etcBERENDSEN)
- {
- please_cite(fplog,"Berendsen84a");
- }
- if (ir->etc == etcVRESCALE)
- {
- please_cite(fplog,"Bussi2007a");
- }
- }
-
- init_nrnb(nrnb);
-
- if (nfile != -1)
- {
- *outf = init_mdoutf(nfile,fnm,Flags,cr,ir,oenv);
-
- *mdebin = init_mdebin((Flags & MD_APPENDFILES) ? NULL : (*outf)->fp_ene,
- mtop,ir, (*outf)->fp_dhdl);
- }
-
- /* Initiate variables */
- clear_mat(force_vir);
- clear_mat(shake_vir);
- clear_rvec(mu_tot);
-
- debug_gmx();
-}
-
+++ /dev/null
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
- *
- *
- * This source code is part of
- *
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
- *
- * VERSION 3.2.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2004, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * If you want to redistribute modifications, 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 www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
- *
- * And Hey:
- * GROwing Monsters And Cloning Shrimps
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <time.h>
-#include <math.h>
-#include "sysstuff.h"
-#include "string2.h"
-#include "network.h"
-#include "confio.h"
-#include "copyrite.h"
-#include "smalloc.h"
-#include "nrnb.h"
-#include "main.h"
-#include "chargegroup.h"
-#include "force.h"
-#include "macros.h"
-#include "random.h"
-#include "names.h"
-#include "gmx_fatal.h"
-#include "txtdump.h"
-#include "typedefs.h"
-#include "update.h"
-#include "random.h"
-#include "constr.h"
-#include "vec.h"
-#include "statutil.h"
-#include "tgroup.h"
-#include "mdebin.h"
-#include "vsite.h"
-#include "force.h"
-#include "mdrun.h"
-#include "domdec.h"
-#include "partdec.h"
-#include "gmx_random.h"
-#include "physics.h"
-#include "xvgr.h"
-#include "mdatoms.h"
-#include "ns.h"
-#include "gmx_wallcycle.h"
-#include "mtop_util.h"
-#include "gmxfio.h"
-#include "pme.h"
-#include "gbutil.h"
-
-#if ( defined(GMX_IA32_SSE) || defined(GMX_X86_64_SSE) || defined(GMX_X86_64_SSE2) )
-#if defined(GMX_DOUBLE)
-#include "gmx_sse2_double.h"
-#else
-#include "gmx_sse2_single.h"
-#endif
-#endif
-
-
-static void global_max(t_commrec *cr,int *n)
-{
- int *sum,i;
-
- snew(sum,cr->nnodes);
- sum[cr->nodeid] = *n;
- gmx_sumi(cr->nnodes,sum,cr);
- for(i=0; i<cr->nnodes; i++)
- *n = max(*n,sum[i]);
-
- sfree(sum);
-}
-
-static void realloc_bins(double **bin,int *nbin,int nbin_new)
-{
- int i;
-
- if (nbin_new != *nbin) {
- srenew(*bin,nbin_new);
- for(i=*nbin; i<nbin_new; i++)
- (*bin)[i] = 0;
- *nbin = nbin_new;
- }
-}
-
-double do_tpi(FILE *fplog,t_commrec *cr,
- int nfile, const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,
- t_inputrec *inputrec,
- gmx_mtop_t *top_global,t_fcdata *fcd,
- t_state *state,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,
- t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime)
-{
- const char *TPI="Test Particle Insertion";
- gmx_localtop_t *top;
- gmx_groups_t *groups;
- gmx_enerdata_t *enerd;
- rvec *f;
- real lambda,t,temp,beta,drmax,epot;
- double embU,sum_embU,*sum_UgembU,V,V_all,VembU_all;
- t_trxstatus *status;
- t_trxframe rerun_fr;
- gmx_bool bDispCorr,bCharge,bRFExcl,bNotLastFrame,bStateChanged,bNS,bOurStep;
- tensor force_vir,shake_vir,vir,pres;
- int cg_tp,a_tp0,a_tp1,ngid,gid_tp,nener,e;
- rvec *x_mol;
- rvec mu_tot,x_init,dx,x_tp;
- int nnodes,frame,nsteps,step;
- int i,start,end;
- gmx_rng_t tpi_rand;
- FILE *fp_tpi=NULL;
- char *ptr,*dump_pdb,**leg,str[STRLEN],str2[STRLEN];
- double dbl,dump_ener;
- gmx_bool bCavity;
- int nat_cavity=0,d;
- real *mass_cavity=NULL,mass_tot;
- int nbin;
- double invbinw,*bin,refvolshift,logV,bUlogV;
- real dvdl,prescorr,enercorr,dvdlcorr;
- gmx_bool bEnergyOutOfBounds;
- const char *tpid_leg[2]={"direct","reweighted"};
-
- /* Since there is no upper limit to the insertion energies,
- * we need to set an upper limit for the distribution output.
- */
- real bU_bin_limit = 50;
- real bU_logV_bin_limit = bU_bin_limit + 10;
-
- nnodes = cr->nnodes;
-
- top = gmx_mtop_generate_local_top(top_global,inputrec);
-
- groups = &top_global->groups;
-
- bCavity = (inputrec->eI == eiTPIC);
- if (bCavity) {
- ptr = getenv("GMX_TPIC_MASSES");
- if (ptr == NULL) {
- nat_cavity = 1;
- } else {
- /* Read (multiple) masses from env var GMX_TPIC_MASSES,
- * The center of mass of the last atoms is then used for TPIC.
- */
- nat_cavity = 0;
- while (sscanf(ptr,"%lf%n",&dbl,&i) > 0) {
- srenew(mass_cavity,nat_cavity+1);
- mass_cavity[nat_cavity] = dbl;
- fprintf(fplog,"mass[%d] = %f\n",
- nat_cavity+1,mass_cavity[nat_cavity]);
- nat_cavity++;
- ptr += i;
- }
- if (nat_cavity == 0)
- gmx_fatal(FARGS,"Found %d masses in GMX_TPIC_MASSES",nat_cavity);
- }
- }
-
- /*
- init_em(fplog,TPI,inputrec,&lambda,nrnb,mu_tot,
- state->box,fr,mdatoms,top,cr,nfile,fnm,NULL,NULL);*/
- /* We never need full pbc for TPI */
- fr->ePBC = epbcXYZ;
- /* Determine the temperature for the Boltzmann weighting */
- temp = inputrec->opts.ref_t[0];
- if (fplog) {
- for(i=1; (i<inputrec->opts.ngtc); i++) {
- if (inputrec->opts.ref_t[i] != temp) {
- fprintf(fplog,"\nWARNING: The temperatures of the different temperature coupling groups are not identical\n\n");
- fprintf(stderr,"\nWARNING: The temperatures of the different temperature coupling groups are not identical\n\n");
- }
- }
- fprintf(fplog,
- "\n The temperature for test particle insertion is %.3f K\n\n",
- temp);
- }
- beta = 1.0/(BOLTZ*temp);
-
- /* Number of insertions per frame */
- nsteps = inputrec->nsteps;
-
- /* Use the same neighborlist with more insertions points
- * in a sphere of radius drmax around the initial point
- */
- /* This should be a proper mdp parameter */
- drmax = inputrec->rtpi;
-
- /* An environment variable can be set to dump all configurations
- * to pdb with an insertion energy <= this value.
- */
- dump_pdb = getenv("GMX_TPI_DUMP");
- dump_ener = 0;
- if (dump_pdb)
- sscanf(dump_pdb,"%lf",&dump_ener);
-
- atoms2md(top_global,inputrec,0,NULL,0,top_global->natoms,mdatoms);
- update_mdatoms(mdatoms,inputrec->init_lambda);
-
- snew(enerd,1);
- init_enerdata(groups->grps[egcENER].nr,inputrec->n_flambda,enerd);
- snew(f,top_global->natoms);
-
- /* Print to log file */
- runtime_start(runtime);
- print_date_and_time(fplog,cr->nodeid,
- "Started Test Particle Insertion",runtime);
- wallcycle_start(wcycle,ewcRUN);
-
- /* The last charge group is the group to be inserted */
- cg_tp = top->cgs.nr - 1;
- a_tp0 = top->cgs.index[cg_tp];
- a_tp1 = top->cgs.index[cg_tp+1];
- if (debug)
- fprintf(debug,"TPI cg %d, atoms %d-%d\n",cg_tp,a_tp0,a_tp1);
- if (a_tp1 - a_tp0 > 1 &&
- (inputrec->rlist < inputrec->rcoulomb ||
- inputrec->rlist < inputrec->rvdw))
- gmx_fatal(FARGS,"Can not do TPI for multi-atom molecule with a twin-range cut-off");
- snew(x_mol,a_tp1-a_tp0);
-
- bDispCorr = (inputrec->eDispCorr != edispcNO);
- bCharge = FALSE;
- for(i=a_tp0; i<a_tp1; i++) {
- /* Copy the coordinates of the molecule to be insterted */
- copy_rvec(state->x[i],x_mol[i-a_tp0]);
- /* Check if we need to print electrostatic energies */
- bCharge |= (mdatoms->chargeA[i] != 0 ||
- (mdatoms->chargeB && mdatoms->chargeB[i] != 0));
- }
- bRFExcl = (bCharge && EEL_RF(fr->eeltype) && fr->eeltype!=eelRF_NEC);
-
- calc_cgcm(fplog,cg_tp,cg_tp+1,&(top->cgs),state->x,fr->cg_cm);
- if (bCavity) {
- if (norm(fr->cg_cm[cg_tp]) > 0.5*inputrec->rlist && fplog) {
- fprintf(fplog, "WARNING: Your TPI molecule is not centered at 0,0,0\n");
- fprintf(stderr,"WARNING: Your TPI molecule is not centered at 0,0,0\n");
- }
- } else {
- /* Center the molecule to be inserted at zero */
- for(i=0; i<a_tp1-a_tp0; i++)
- rvec_dec(x_mol[i],fr->cg_cm[cg_tp]);
- }
-
- if (fplog) {
- fprintf(fplog,"\nWill insert %d atoms %s partial charges\n",
- a_tp1-a_tp0,bCharge ? "with" : "without");
-
- fprintf(fplog,"\nWill insert %d times in each frame of %s\n",
- nsteps,opt2fn("-rerun",nfile,fnm));
- }
-
- if (!bCavity)
- {
- if (inputrec->nstlist > 1)
- {
- if (drmax==0 && a_tp1-a_tp0==1)
- {
- gmx_fatal(FARGS,"Re-using the neighborlist %d times for insertions of a single atom in a sphere of radius %f does not make sense",inputrec->nstlist,drmax);
- }
- if (fplog)
- {
- fprintf(fplog,"Will use the same neighborlist for %d insertions in a sphere of radius %f\n",inputrec->nstlist,drmax);
- }
- }
- }
- else
- {
- if (fplog)
- {
- fprintf(fplog,"Will insert randomly in a sphere of radius %f around the center of the cavity\n",drmax);
- }
- }
-
- ngid = groups->grps[egcENER].nr;
- gid_tp = GET_CGINFO_GID(fr->cginfo[cg_tp]);
- nener = 1 + ngid;
- if (bDispCorr)
- nener += 1;
- if (bCharge) {
- nener += ngid;
- if (bRFExcl)
- nener += 1;
- if (EEL_FULL(fr->eeltype))
- nener += 1;
- }
- snew(sum_UgembU,nener);
-
- /* Initialize random generator */
- tpi_rand = gmx_rng_init(inputrec->ld_seed);
-
- if (MASTER(cr)) {
- fp_tpi = xvgropen(opt2fn("-tpi",nfile,fnm),
- "TPI energies","Time (ps)",
- "(kJ mol\\S-1\\N) / (nm\\S3\\N)",oenv);
- xvgr_subtitle(fp_tpi,"f. are averages over one frame",oenv);
- snew(leg,4+nener);
- e = 0;
- sprintf(str,"-kT log(<Ve\\S-\\betaU\\N>/<V>)");
- leg[e++] = strdup(str);
- sprintf(str,"f. -kT log<e\\S-\\betaU\\N>");
- leg[e++] = strdup(str);
- sprintf(str,"f. <e\\S-\\betaU\\N>");
- leg[e++] = strdup(str);
- sprintf(str,"f. V");
- leg[e++] = strdup(str);
- sprintf(str,"f. <Ue\\S-\\betaU\\N>");
- leg[e++] = 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);
- }
- if (bDispCorr) {
- sprintf(str,"f. <U\\sdisp c\\Ne\\S-\\betaU\\N>");
- leg[e++] = strdup(str);
- }
- if (bCharge) {
- for(i=0; i<ngid; i++) {
- sprintf(str,"f. <U\\sCoul %s\\Ne\\S-\\betaU\\N>",
- *(groups->grpname[groups->grps[egcENER].nm_ind[i]]));
- leg[e++] = strdup(str);
- }
- if (bRFExcl) {
- sprintf(str,"f. <U\\sRF excl\\Ne\\S-\\betaU\\N>");
- leg[e++] = strdup(str);
- }
- if (EEL_FULL(fr->eeltype)) {
- sprintf(str,"f. <U\\sCoul recip\\Ne\\S-\\betaU\\N>");
- leg[e++] = strdup(str);
- }
- }
- xvgr_legend(fp_tpi,4+nener,(const char**)leg,oenv);
- for(i=0; i<4+nener; i++)
- sfree(leg[i]);
- sfree(leg);
- }
- clear_rvec(x_init);
- V_all = 0;
- VembU_all = 0;
-
- invbinw = 10;
- nbin = 10;
- snew(bin,nbin);
-
- bNotLastFrame = read_first_frame(oenv,&status,opt2fn("-rerun",nfile,fnm),
- &rerun_fr,TRX_NEED_X);
- frame = 0;
-
- if (rerun_fr.natoms - (bCavity ? nat_cavity : 0) !=
- mdatoms->nr - (a_tp1 - a_tp0))
- gmx_fatal(FARGS,"Number of atoms in trajectory (%d)%s "
- "is not equal the number in the run input file (%d) "
- "minus the number of atoms to insert (%d)\n",
- rerun_fr.natoms,bCavity ? " minus one" : "",
- mdatoms->nr,a_tp1-a_tp0);
-
- refvolshift = log(det(rerun_fr.box));
-
-#if ( defined(GMX_IA32_SSE) || defined(GMX_X86_64_SSE) || defined(GMX_X86_64_SSE2) )
- /* Make sure we don't detect SSE overflow generated before this point */
- gmx_mm_check_and_reset_overflow();
-#endif
-
- while (bNotLastFrame)
- {
- lambda = rerun_fr.lambda;
- t = rerun_fr.time;
-
- sum_embU = 0;
- for(e=0; e<nener; e++)
- {
- sum_UgembU[e] = 0;
- }
-
- /* Copy the coordinates from the input trajectory */
- for(i=0; i<rerun_fr.natoms; i++)
- {
- copy_rvec(rerun_fr.x[i],state->x[i]);
- }
-
- V = det(rerun_fr.box);
- logV = log(V);
-
- bStateChanged = TRUE;
- bNS = TRUE;
- for(step=0; step<nsteps; step++)
- {
- /* In parallel all nodes generate all random configurations.
- * In that way the result is identical to a single cpu tpi run.
- */
- if (!bCavity)
- {
- /* Random insertion in the whole volume */
- bNS = (step % inputrec->nstlist == 0);
- if (bNS)
- {
- /* Generate a random position in the box */
- x_init[XX] = gmx_rng_uniform_real(tpi_rand)*state->box[XX][XX];
- x_init[YY] = gmx_rng_uniform_real(tpi_rand)*state->box[YY][YY];
- x_init[ZZ] = gmx_rng_uniform_real(tpi_rand)*state->box[ZZ][ZZ];
- }
- if (inputrec->nstlist == 1)
- {
- copy_rvec(x_init,x_tp);
- }
- else
- {
- /* Generate coordinates within |dx|=drmax of x_init */
- do
- {
- dx[XX] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
- dx[YY] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
- dx[ZZ] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
- }
- while (norm2(dx) > drmax*drmax);
- rvec_add(x_init,dx,x_tp);
- }
- }
- else
- {
- /* Random insertion around a cavity location
- * given by the last coordinate of the trajectory.
- */
- if (step == 0)
- {
- if (nat_cavity == 1)
- {
- /* Copy the location of the cavity */
- copy_rvec(rerun_fr.x[rerun_fr.natoms-1],x_init);
- }
- else
- {
- /* Determine the center of mass of the last molecule */
- clear_rvec(x_init);
- mass_tot = 0;
- for(i=0; i<nat_cavity; i++)
- {
- for(d=0; d<DIM; d++)
- {
- x_init[d] +=
- mass_cavity[i]*rerun_fr.x[rerun_fr.natoms-nat_cavity+i][d];
- }
- mass_tot += mass_cavity[i];
- }
- for(d=0; d<DIM; d++)
- {
- x_init[d] /= mass_tot;
- }
- }
- }
- /* Generate coordinates within |dx|=drmax of x_init */
- do
- {
- dx[XX] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
- dx[YY] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
- dx[ZZ] = (2*gmx_rng_uniform_real(tpi_rand) - 1)*drmax;
- }
- while (norm2(dx) > drmax*drmax);
- rvec_add(x_init,dx,x_tp);
- }
-
- if (a_tp1 - a_tp0 == 1)
- {
- /* Insert a single atom, just copy the insertion location */
- copy_rvec(x_tp,state->x[a_tp0]);
- }
- else
- {
- /* Copy the coordinates from the top file */
- for(i=a_tp0; i<a_tp1; i++)
- {
- copy_rvec(x_mol[i-a_tp0],state->x[i]);
- }
- /* Rotate the molecule randomly */
- rotate_conf(a_tp1-a_tp0,state->x+a_tp0,NULL,
- 2*M_PI*gmx_rng_uniform_real(tpi_rand),
- 2*M_PI*gmx_rng_uniform_real(tpi_rand),
- 2*M_PI*gmx_rng_uniform_real(tpi_rand));
- /* Shift to the insertion location */
- for(i=a_tp0; i<a_tp1; i++)
- {
- rvec_inc(state->x[i],x_tp);
- }
- }
-
- /* Check if this insertion belongs to this node */
- bOurStep = TRUE;
- if (PAR(cr))
- {
- switch (inputrec->eI)
- {
- case eiTPI:
- bOurStep = ((step / inputrec->nstlist) % nnodes == cr->nodeid);
- break;
- case eiTPIC:
- bOurStep = (step % nnodes == cr->nodeid);
- break;
- default:
- gmx_fatal(FARGS,"Unknown integrator %s",ei_names[inputrec->eI]);
- }
- }
- if (bOurStep)
- {
- /* Clear some matrix variables */
- clear_mat(force_vir);
- clear_mat(shake_vir);
- clear_mat(vir);
- clear_mat(pres);
-
- /* Set the charge group center of mass of the test particle */
- copy_rvec(x_init,fr->cg_cm[top->cgs.nr-1]);
-
- /* Calc energy (no forces) on new positions.
- * Since we only need the intermolecular energy
- * and the RF exclusion terms of the inserted molecule occur
- * within a single charge group we can pass NULL for the graph.
- * This also avoids shifts that would move charge groups
- * out of the box.
- *
- * Some checks above ensure than we can not have
- * twin-range interactions together with nstlist > 1,
- * therefore we do not need to remember the LR energies.
- */
- /* Make do_force do a single node force calculation */
- cr->nnodes = 1;
- do_force(fplog,cr,inputrec,
- step,nrnb,wcycle,top,top_global,&top_global->groups,
- rerun_fr.box,state->x,&state->hist,
- f,force_vir,mdatoms,enerd,fcd,
- lambda,NULL,fr,NULL,mu_tot,t,NULL,NULL,FALSE,
- GMX_FORCE_NONBONDED |
- (bNS ? GMX_FORCE_NS | GMX_FORCE_DOLR : 0) |
- (bStateChanged ? GMX_FORCE_STATECHANGED : 0));
- cr->nnodes = nnodes;
- bStateChanged = FALSE;
- bNS = FALSE;
-
- /* Calculate long range corrections to pressure and energy */
- calc_dispcorr(fplog,inputrec,fr,step,top_global->natoms,rerun_fr.box,
- lambda,pres,vir,&prescorr,&enercorr,&dvdlcorr);
- /* figure out how to rearrange the next 4 lines MRS 8/4/2009 */
- enerd->term[F_DISPCORR] = enercorr;
- enerd->term[F_EPOT] += enercorr;
- enerd->term[F_PRES] += prescorr;
- enerd->term[F_DVDL] += dvdlcorr;
-
- epot = enerd->term[F_EPOT];
- bEnergyOutOfBounds = FALSE;
-#if ( defined(GMX_IA32_SSE) || defined(GMX_X86_64_SSE) || defined(GMX_X86_64_SSE2) )
- /* With SSE the energy can overflow, check for this */
- if (gmx_mm_check_and_reset_overflow())
- {
- if (debug)
- {
- fprintf(debug,"Found an SSE overflow, assuming the energy is out of bounds\n");
- }
- bEnergyOutOfBounds = TRUE;
- }
-#endif
- /* If the compiler doesn't optimize this check away
- * we catch the NAN energies.
- * The epot>GMX_REAL_MAX check catches inf values,
- * which should nicely result in embU=0 through the exp below,
- * but it does not hurt to check anyhow.
- */
- /* Non-bonded Interaction usually diverge at r=0.
- * With tabulated interaction functions the first few entries
- * should be capped in a consistent fashion between
- * repulsion, dispersion and Coulomb to avoid accidental
- * negative values in the total energy.
- * The table generation code in tables.c does this.
- * With user tbales the user should take care of this.
- */
- if (epot != epot || epot > GMX_REAL_MAX)
- {
- bEnergyOutOfBounds = TRUE;
- }
- if (bEnergyOutOfBounds)
- {
- if (debug)
- {
- fprintf(debug,"\n time %.3f, step %d: non-finite energy %f, using exp(-bU)=0\n",t,step,epot);
- }
- embU = 0;
- }
- else
- {
- embU = exp(-beta*epot);
- sum_embU += embU;
- /* Determine the weighted energy contributions of each energy group */
- e = 0;
- sum_UgembU[e++] += epot*embU;
- if (fr->bBHAM)
- {
- for(i=0; i<ngid; i++)
- {
- sum_UgembU[e++] +=
- (enerd->grpp.ener[egBHAMSR][GID(i,gid_tp,ngid)] +
- enerd->grpp.ener[egBHAMLR][GID(i,gid_tp,ngid)])*embU;
- }
- }
- else
- {
- for(i=0; i<ngid; i++)
- {
- sum_UgembU[e++] +=
- (enerd->grpp.ener[egLJSR][GID(i,gid_tp,ngid)] +
- enerd->grpp.ener[egLJLR][GID(i,gid_tp,ngid)])*embU;
- }
- }
- if (bDispCorr)
- {
- sum_UgembU[e++] += enerd->term[F_DISPCORR]*embU;
- }
- if (bCharge)
- {
- for(i=0; i<ngid; i++)
- {
- sum_UgembU[e++] +=
- (enerd->grpp.ener[egCOULSR][GID(i,gid_tp,ngid)] +
- enerd->grpp.ener[egCOULLR][GID(i,gid_tp,ngid)])*embU;
- }
- if (bRFExcl)
- {
- sum_UgembU[e++] += enerd->term[F_RF_EXCL]*embU;
- }
- if (EEL_FULL(fr->eeltype))
- {
- sum_UgembU[e++] += enerd->term[F_COUL_RECIP]*embU;
- }
- }
- }
-
- if (embU == 0 || beta*epot > bU_bin_limit)
- {
- bin[0]++;
- }
- else
- {
- i = (int)((bU_logV_bin_limit
- - (beta*epot - logV + refvolshift))*invbinw
- + 0.5);
- if (i < 0)
- {
- i = 0;
- }
- if (i >= nbin)
- {
- realloc_bins(&bin,&nbin,i+10);
- }
- bin[i]++;
- }
-
- if (debug)
- {
- fprintf(debug,"TPI %7d %12.5e %12.5f %12.5f %12.5f\n",
- step,epot,x_tp[XX],x_tp[YY],x_tp[ZZ]);
- }
-
- if (dump_pdb && epot <= dump_ener)
- {
- sprintf(str,"t%g_step%d.pdb",t,step);
- sprintf(str2,"t: %f step %d ener: %f",t,step,epot);
- write_sto_conf_mtop(str,str2,top_global,state->x,state->v,
- inputrec->ePBC,state->box);
- }
- }
- }
-
- if (PAR(cr))
- {
- /* When running in parallel sum the energies over the processes */
- gmx_sumd(1, &sum_embU, cr);
- gmx_sumd(nener,sum_UgembU,cr);
- }
-
- frame++;
- V_all += V;
- VembU_all += V*sum_embU/nsteps;
-
- if (fp_tpi)
- {
- if (bVerbose || frame%10==0 || frame<10)
- {
- fprintf(stderr,"mu %10.3e <mu> %10.3e\n",
- -log(sum_embU/nsteps)/beta,-log(VembU_all/V_all)/beta);
- }
-
- fprintf(fp_tpi,"%10.3f %12.5e %12.5e %12.5e %12.5e",
- t,
- VembU_all==0 ? 20/beta : -log(VembU_all/V_all)/beta,
- sum_embU==0 ? 20/beta : -log(sum_embU/nsteps)/beta,
- sum_embU/nsteps,V);
- for(e=0; e<nener; e++)
- {
- fprintf(fp_tpi," %12.5e",sum_UgembU[e]/nsteps);
- }
- fprintf(fp_tpi,"\n");
- fflush(fp_tpi);
- }
-
- bNotLastFrame = read_next_frame(oenv, status,&rerun_fr);
- } /* End of the loop */
- runtime_end(runtime);
-
- close_trj(status);
-
- if (fp_tpi != NULL)
- {
- gmx_fio_fclose(fp_tpi);
- }
-
- if (fplog != NULL)
- {
- fprintf(fplog,"\n");
- fprintf(fplog," <V> = %12.5e nm^3\n",V_all/frame);
- fprintf(fplog," <mu> = %12.5e kJ/mol\n",-log(VembU_all/V_all)/beta);
- }
-
- /* Write the Boltzmann factor histogram */
- if (PAR(cr))
- {
- /* When running in parallel sum the bins over the processes */
- i = nbin;
- global_max(cr,&i);
- realloc_bins(&bin,&nbin,i);
- gmx_sumd(nbin,bin,cr);
- }
- if (MASTER(cr))
- {
- fp_tpi = xvgropen(opt2fn("-tpid",nfile,fnm),
- "TPI energy distribution",
- "\\betaU - log(V/<V>)","count",oenv);
- sprintf(str,"number \\betaU > %g: %9.3e",bU_bin_limit,bin[0]);
- xvgr_subtitle(fp_tpi,str,oenv);
- xvgr_legend(fp_tpi,2,(const char **)tpid_leg,oenv);
- for(i=nbin-1; i>0; i--)
- {
- bUlogV = -i/invbinw + bU_logV_bin_limit - refvolshift + log(V_all/frame);
- fprintf(fp_tpi,"%6.2f %10d %12.5e\n",
- bUlogV,
- (int)(bin[i]+0.5),
- bin[i]*exp(-bUlogV)*V_all/VembU_all);
- }
- gmx_fio_fclose(fp_tpi);
- }
- sfree(bin);
-
- sfree(sum_UgembU);
-
- runtime->nsteps_done = frame*inputrec->nsteps;
-
- return 0;
-}
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
\ No newline at end of file
.deps
.libs
+g_highway
+g_logo
+g_xrama
+ngmx
--- /dev/null
+add_subdirectory(g_ana)
--- /dev/null
+add_executable(g_ana g_ana.cpp)
+target_link_libraries(g_ana libgromacs)
+set_target_properties(g_ana PROPERTIES OUTPUT_NAME "g_ana${GMX_BINARY_SUFFIX}")
+
+install(TARGETS g_ana
+ RUNTIME DESTINATION ${BIN_INSTALL_DIR})
--- /dev/null
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \brief
+ * Implements the g_ana tool.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ */
+#include <copyrite.h>
+
+#include "gromacs/fatalerror.h"
+#include "gromacs/trajectoryanalysis/cmdlinerunner.h"
+#include "gromacs/trajectoryanalysis/modules.h"
+
+int
+main(int argc, char *argv[])
+{
+ if (argc < 2)
+ {
+ CopyRight(stderr, argv[0]);
+ GMX_ERROR(gmx::eeInvalidInput,
+ "Not enough command-line arguments");
+ }
+
+ gmx::TrajectoryAnalysisModule *mod
+ = gmx::createTrajectoryAnalysisModule(argv[1]);
+ if (mod == NULL)
+ {
+ CopyRight(stderr, argv[0]);
+ GMX_ERROR(gmx::eeInvalidInput,
+ "Unknown analysis module given as the first command-line argument");
+ }
+ --argc;
+ ++argv;
+
+ gmx::TrajectoryAnalysisCommandLineRunner runner(mod);
+ return runner.run(argc, argv);
+}
+++ /dev/null
-Makefile
-Makefile.in
-.deps
-.libs
\ No newline at end of file
.deps
.libs
+do_dssp
+editconf
+eneconv
+g_anadock
+g_anaeig
+g_analyze
+g_angle
+g_bar
+g_bond
+g_bundle
+g_chi
+g_cluster
+g_clustsize
+g_confrms
+g_covar
+g_current
+g_density
+g_densmap
+g_dielectric
+g_dih
+g_dipoles
+g_disre
+g_dist
+g_dyndom
+g_enemat
+g_energy
+g_filter
+g_gyrate
+g_h2order
+g_hbond
+g_helix
+g_helixorient
+g_kinetics
+g_lie
+g_mdmat
+g_membed
+g_mindist
+g_morph
+g_msd
+g_nmeig
+g_nmens
+g_nmtraj
+g_order
+g_polystat
+g_potential
+g_principal
+g_rama
+g_rdf
+g_rms
+g_rmsdist
+g_rmsf
+g_rotacf
+g_rotmat
+g_saltbr
+g_sas
+g_sdf
+g_select
+g_sgangle
+g_sham
+g_sigeps
+g_sorient
+g_spatial
+g_spol
+g_tcaf
+g_traj
+g_tune_pme
+g_vanhove
+g_velacc
+g_wham
+g_wheel
+genbox
+genconf
+genion
+genrestr
+make_edi
+make_ndx
+mk_angndx
+trjcat
+trjconv
+trjorder
+xpm2ps
-
add_library(gmxana
autocorr.c expfit.c polynomials.c levenmar.c
anadih.c pp2shift.c dlist.c
gmx_polystat.c gmx_potential.c gmx_rama.c
gmx_rdf.c gmx_rms.c gmx_rmsf.c
gmx_rotacf.c gmx_saltbr.c gmx_sas.c
- gmx_select.c gmx_rmsdist.c gmx_rotmat.c
+ gmx_rmsdist.c gmx_rotmat.c
gmx_sgangle.c gmx_sorient.c gmx_spol.c gmx_tcaf.c
gmx_traj.c gmx_velacc.c gmx_helixorient.c
gmx_clustsize.c gmx_mdmat.c gmx_wham.c
gmx_editconf.c gmx_genbox.c gmx_genion.c gmx_genconf.c
gmx_genpr.c gmx_eneconv.c gmx_vanhove.c gmx_wheel.c
addconf.c calcpot.c edittop.c gmx_bar.c
- gmx_membed.c gmx_pme_error.c )
+ gmx_pme_error.c )
-target_link_libraries(gmxana md gmx)
+target_link_libraries(gmxana libgromacs)
set_target_properties(gmxana PROPERTIES OUTPUT_NAME "gmxana${GMX_LIBS_SUFFIX}" SOVERSION ${SOVERSION} INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
# List of programs with single corresponding .c source file,
g_dyndom g_enemat g_energy g_lie g_filter g_gyrate
g_h2order g_hbond g_helix g_mindist g_msd g_morph g_nmeig
g_nmens g_order g_kinetics g_polystat g_potential g_rama g_rdf g_rms
- g_rmsf g_rotacf g_saltbr g_sas g_select g_sgangle g_sham g_sorient
+ g_rmsf g_rotacf g_saltbr g_sas g_sgangle g_sham g_sorient
g_spol g_spatial g_tcaf g_traj g_tune_pme g_vanhove
g_velacc g_clustsize g_mdmat g_wham g_sigeps g_bar
- g_membed g_pme_error g_rmsdist g_rotmat)
+ g_pme_error g_rmsdist g_rotmat)
gmx_polystat.c gmx_potential.c gmx_rama.c \
gmx_rdf.c gmx_rms.c gmx_rmsdist.c gmx_rmsf.c \
gmx_rotacf.c gmx_rotmat.c gmx_saltbr.c gmx_sas.c \
- gmx_select.c gmx_pme_error.c \
+ gmx_select.c gmx_pme_error.c gmx_membed.c \
gmx_sgangle.c gmx_sorient.c gmx_spol.c gmx_tcaf.c \
gmx_traj.c gmx_velacc.c gmx_helixorient.c \
gmx_clustsize.c gmx_mdmat.c gmx_wham.c eigio.h \
gmx_trjconv.c gmx_trjcat.c gmx_trjorder.c gmx_xpm2ps.c \
gmx_editconf.c gmx_genbox.c gmx_genion.c gmx_genconf.c \
gmx_genpr.c gmx_eneconv.c gmx_vanhove.c gmx_wheel.c \
- addconf.c addconf.h gmx_tune_pme.c gmx_membed.c \
+ addconf.c addconf.h gmx_tune_pme.c \
calcpot.c calcpot.h edittop.c
bin_PROGRAMS = \
g_enemat g_energy g_lie g_filter \
g_gyrate g_h2order g_hbond g_helix \
g_mindist g_msd g_morph g_nmeig \
- g_nmens g_order g_kinetics \
+ g_nmens g_order g_kinetics g_membed \
g_polystat g_potential g_rama \
g_rdf g_rms g_rmsdist g_rmsf \
g_rotacf g_rotmat g_saltbr g_sas \
g_sham g_sorient g_spol \
g_spatial g_pme_error \
g_tcaf g_traj g_tune_pme \
- g_vanhove g_velacc g_membed \
+ g_vanhove g_velacc \
g_clustsize g_mdmat g_wham \
g_sigeps
#include <signal.h>
#include <stdlib.h>
#include "typedefs.h"
-#include "smalloc.h"
#include "sysstuff.h"
-#include "vec.h"
#include "statutil.h"
#include "macros.h"
#include "copyrite.h"
#include "main.h"
-#include "futil.h"
-#include "edsam.h"
-#include "checkpoint.h"
-#include "vcm.h"
-#include "mdebin.h"
-#include "nrnb.h"
-#include "calcmu.h"
-#include "index.h"
-#include "vsite.h"
-#include "update.h"
-#include "ns.h"
-#include "trnio.h"
-#include "xtcio.h"
-#include "mdrun.h"
-#include "confio.h"
-#include "network.h"
-#include "pull.h"
-#include "xvgr.h"
-#include "physics.h"
-#include "names.h"
-#include "disre.h"
-#include "orires.h"
-#include "dihre.h"
-#include "pppm.h"
-#include "pme.h"
-#include "mdatoms.h"
-#include "qmmm.h"
-#include "mpelogging.h"
-#include "domdec.h"
-#include "partdec.h"
-#include "topsort.h"
-#include "coulomb.h"
-#include "constr.h"
-#include "shellfc.h"
-#include "mvdata.h"
-#include "checkpoint.h"
-#include "mtop_util.h"
-#include "tpxio.h"
-#include "string2.h"
-#include "sighandler.h"
#include "gmx_ana.h"
-#ifdef GMX_LIB_MPI
-#include <mpi.h>
-#endif
-#ifdef GMX_THREADS
-#include "tmpi.h"
-#endif
-
-/* afm stuf */
-#include "pull.h"
-
-/* We use the same defines as in mvdata.c here */
-#define block_bc(cr, d) gmx_bcast( sizeof(d), &(d),(cr))
-#define nblock_bc(cr,nr,d) gmx_bcast((nr)*sizeof((d)[0]), (d),(cr))
-#define snew_bc(cr,d,nr) { if (!MASTER(cr)) snew((d),(nr)); }
-
-/* The following two variables and the signal_handler function
- * is used from pme.c as well
- */
-
-typedef struct {
- t_state s;
- rvec *f;
- real epot;
- real fnorm;
- real fmax;
- int a_fmax;
-} em_state_t;
-
-typedef struct {
- int it_xy;
- int it_z;
- int xy_step;
- int z_step;
- rvec xmin;
- rvec xmax;
- rvec *geom_cent;
- int pieces;
- int *nidx;
- atom_id **subindex;
-} pos_ins_t;
-
-typedef struct {
- int id;
- char *name;
- int nr;
- int natoms; /*nr of atoms per lipid*/
- int mol1; /*id of the first lipid molecule*/
- real area;
-} lip_t;
-
-typedef struct {
- char *name;
- t_block mem_at;
- int *mol_id;
- int nmol;
- real lip_area;
- real zmin;
- real zmax;
- real zmed;
-} mem_t;
-
-typedef struct {
- int *mol;
- int *block;
- int nr;
-} rm_t;
-
-int search_string(char *s,int ng,char ***gn)
-{
- int i;
-
- for(i=0; (i<ng); i++)
- if (gmx_strcasecmp(s,*gn[i]) == 0)
- return i;
-
- gmx_fatal(FARGS,"Group %s not found in indexfile.\nMaybe you have non-default groups in your mdp file, while not using the '-n' option of grompp.\nIn that case use the '-n' option.\n",s);
-
- return -1;
-}
-
-int get_mol_id(int at,int nmblock,gmx_molblock_t *mblock, int *type, int *block)
-{
- int mol_id=0;
- int i;
-
- for(i=0;i<nmblock;i++)
- {
- if(at<(mblock[i].nmol*mblock[i].natoms_mol))
- {
- mol_id+=at/mblock[i].natoms_mol;
- *type = mblock[i].type;
- *block = i;
- return mol_id;
- } else {
- at-= mblock[i].nmol*mblock[i].natoms_mol;
- mol_id+=mblock[i].nmol;
- }
- }
-
- gmx_fatal(FARGS,"Something is wrong in mol ids, at %d, mol_id %d",at,mol_id);
-
- return -1;
-}
-
-int get_block(int mol_id,int nmblock,gmx_molblock_t *mblock)
-{
- int i;
- int nmol=0;
-
- for(i=0;i<nmblock;i++)
- {
- nmol+=mblock[i].nmol;
- if(mol_id<nmol)
- return i;
- }
-
- gmx_fatal(FARGS,"mol_id %d larger than total number of molecules %d.\n",mol_id,nmol);
-
- return -1;
-}
-
-int get_tpr_version(const char *infile)
-{
- char buf[STRLEN];
- gmx_bool bDouble;
- int precision,fver;
- t_fileio *fio;
-
- fio = open_tpx(infile,"r");
- gmx_fio_checktype(fio);
-
- precision = sizeof(real);
-
- gmx_fio_do_string(fio,buf);
- if (strncmp(buf,"VERSION",7))
- gmx_fatal(FARGS,"Can not read file %s,\n"
- " this file is from a Gromacs version which is older than 2.0\n"
- " Make a new one with grompp or use a gro or pdb file, if possible",
- gmx_fio_getname(fio));
- gmx_fio_do_int(fio,precision);
- bDouble = (precision == sizeof(double));
- if ((precision != sizeof(float)) && !bDouble)
- gmx_fatal(FARGS,"Unknown precision in file %s: real is %d bytes "
- "instead of %d or %d",
- gmx_fio_getname(fio),precision,sizeof(float),sizeof(double));
- gmx_fio_setprecision(fio,bDouble);
- fprintf(stderr,"Reading file %s, %s (%s precision)\n",
- gmx_fio_getname(fio),buf,bDouble ? "double" : "single");
-
- gmx_fio_do_int(fio,fver);
-
- close_tpx(fio);
-
- return fver;
-}
-
-void set_inbox(int natom, rvec *x)
-{
- rvec tmp;
- int i;
-
- tmp[XX]=tmp[YY]=tmp[ZZ]=0.0;
- for(i=0;i<natom;i++)
- {
- if(x[i][XX]<tmp[XX]) tmp[XX]=x[i][XX];
- if(x[i][YY]<tmp[YY]) tmp[YY]=x[i][YY];
- if(x[i][ZZ]<tmp[ZZ]) tmp[ZZ]=x[i][ZZ];
- }
-
- for(i=0;i<natom;i++)
- rvec_inc(x[i],tmp);
-}
-
-int get_mtype_list(t_block *at, gmx_mtop_t *mtop, t_block *tlist)
-{
- int i,j,nr,mol_id;
- int type=0,block=0;
- gmx_bool bNEW;
-
- nr=0;
- snew(tlist->index,at->nr);
- for (i=0;i<at->nr;i++)
- {
- bNEW=TRUE;
- mol_id = get_mol_id(at->index[i],mtop->nmolblock,mtop->molblock,&type,&block);
- for(j=0;j<nr;j++)
- {
- if(tlist->index[j]==type)
- bNEW=FALSE;
- }
- if(bNEW==TRUE)
- {
- tlist->index[nr]=type;
- nr++;
- }
- }
-
- srenew(tlist->index,nr);
- return nr;
-}
-
-void check_types(t_block *ins_at,t_block *rest_at,gmx_mtop_t *mtop)
-{
- t_block *ins_mtype,*rest_mtype;
- int i,j;
-
- snew(ins_mtype,1);
- snew(rest_mtype,1);
- ins_mtype->nr = get_mtype_list(ins_at , mtop, ins_mtype );
- rest_mtype->nr = get_mtype_list(rest_at, mtop, rest_mtype);
-
- for(i=0;i<ins_mtype->nr;i++)
- {
- for(j=0;j<rest_mtype->nr;j++)
- {
- if(ins_mtype->index[i]==rest_mtype->index[j])
- gmx_fatal(FARGS,"Moleculetype %s is found both in the group to insert and the rest of the system.\n"
- "Because we need to exclude all interactions between the atoms in the group to\n"
- "insert, the same moleculetype can not be used in both groups. Change the\n"
- "moleculetype of the molecules %s in the inserted group. Do not forget to provide\n"
- "an appropriate *.itp file",*(mtop->moltype[rest_mtype->index[j]].name),
- *(mtop->moltype[rest_mtype->index[j]].name));
- }
- }
-
- sfree(ins_mtype->index);
- sfree(rest_mtype->index);
- sfree(ins_mtype);
- sfree(rest_mtype);
-}
-
-int init_ins_at(t_block *ins_at,t_block *rest_at,t_state *state, pos_ins_t *pos_ins,gmx_groups_t *groups,int ins_grp_id, real xy_max)
-{
- int i,gid,c=0;
- real x,xmin,xmax,y,ymin,ymax,z,zmin,zmax;
-
- snew(rest_at->index,state->natoms);
-
- xmin=xmax=state->x[ins_at->index[0]][XX];
- ymin=ymax=state->x[ins_at->index[0]][YY];
- zmin=zmax=state->x[ins_at->index[0]][ZZ];
-
- for(i=0;i<state->natoms;i++)
- {
- gid = groups->grpnr[egcFREEZE][i];
- if(groups->grps[egcFREEZE].nm_ind[gid]==ins_grp_id)
- {
- x=state->x[i][XX];
- if (x<xmin) xmin=x;
- if (x>xmax) xmax=x;
- y=state->x[i][YY];
- if (y<ymin) ymin=y;
- if (y>ymax) ymax=y;
- z=state->x[i][ZZ];
- if (z<zmin) zmin=z;
- if (z>zmax) zmax=z;
- } else {
- rest_at->index[c]=i;
- c++;
- }
- }
-
- rest_at->nr=c;
- srenew(rest_at->index,c);
-
- if(xy_max>1.000001)
- {
- pos_ins->xmin[XX]=xmin-((xmax-xmin)*xy_max-(xmax-xmin))/2;
- pos_ins->xmin[YY]=ymin-((ymax-ymin)*xy_max-(ymax-ymin))/2;
-
- pos_ins->xmax[XX]=xmax+((xmax-xmin)*xy_max-(xmax-xmin))/2;
- pos_ins->xmax[YY]=ymax+((ymax-ymin)*xy_max-(ymax-ymin))/2;
- } else {
- pos_ins->xmin[XX]=xmin;
- pos_ins->xmin[YY]=ymin;
-
- pos_ins->xmax[XX]=xmax;
- pos_ins->xmax[YY]=ymax;
- }
-
- /* 6.0 is estimated thickness of bilayer */
- if( (zmax-zmin) < 6.0 )
- {
- pos_ins->xmin[ZZ]=zmin+(zmax-zmin)/2.0-3.0;
- pos_ins->xmax[ZZ]=zmin+(zmax-zmin)/2.0+3.0;
- } else {
- pos_ins->xmin[ZZ]=zmin;
- pos_ins->xmax[ZZ]=zmax;
- }
-
- return c;
-}
-
-real est_prot_area(pos_ins_t *pos_ins,rvec *r,t_block *ins_at, mem_t *mem_p)
-{
- real x,y,dx=0.15,dy=0.15,area=0.0;
- real add;
- int c,at;
-
- for(x=pos_ins->xmin[XX];x<pos_ins->xmax[XX];x+=dx)
- {
- for(y=pos_ins->xmin[YY];y<pos_ins->xmax[YY];y+=dy)
- {
- c=0;
- add=0.0;
- do
- {
- at=ins_at->index[c];
- if ( (r[at][XX]>=x) && (r[at][XX]<x+dx) &&
- (r[at][YY]>=y) && (r[at][YY]<y+dy) &&
- (r[at][ZZ]>mem_p->zmin+1.0) && (r[at][ZZ]<mem_p->zmax-1.0) )
- add=1.0;
- c++;
- } while ( (c<ins_at->nr) && (add<0.5) );
- area+=add;
- }
- }
- area=area*dx*dy;
-
- return area;
-}
-
-void init_lip(matrix box, gmx_mtop_t *mtop, lip_t *lip)
-{
- int i;
- real mem_area;
- int mol1=0;
-
- mem_area = box[XX][XX]*box[YY][YY]-box[XX][YY]*box[YY][XX];
- for(i=0;i<mtop->nmolblock;i++)
- {
- if(mtop->molblock[i].type == lip->id)
- {
- lip->nr=mtop->molblock[i].nmol;
- lip->natoms=mtop->molblock[i].natoms_mol;
- }
- }
- lip->area=2.0*mem_area/(double)lip->nr;
-
- for (i=0;i<lip->id;i++)
- mol1+=mtop->molblock[i].nmol;
- lip->mol1=mol1;
-}
-
-int init_mem_at(mem_t *mem_p, gmx_mtop_t *mtop, rvec *r, matrix box, pos_ins_t *pos_ins)
-{
- int i,j,at,mol,nmol,nmolbox,count;
- t_block *mem_a;
- real z,zmin,zmax,mem_area;
- gmx_bool bNew;
- atom_id *mol_id;
- int type=0,block=0;
-
- nmol=count=0;
- mem_a=&(mem_p->mem_at);
- snew(mol_id,mem_a->nr);
-/* snew(index,mem_a->nr); */
- zmin=pos_ins->xmax[ZZ];
- zmax=pos_ins->xmin[ZZ];
- for(i=0;i<mem_a->nr;i++)
- {
- at=mem_a->index[i];
- if( (r[at][XX]>pos_ins->xmin[XX]) && (r[at][XX]<pos_ins->xmax[XX]) &&
- (r[at][YY]>pos_ins->xmin[YY]) && (r[at][YY]<pos_ins->xmax[YY]) &&
- (r[at][ZZ]>pos_ins->xmin[ZZ]) && (r[at][ZZ]<pos_ins->xmax[ZZ]) )
- {
- mol = get_mol_id(at,mtop->nmolblock,mtop->molblock,&type,&block);
-
- bNew=TRUE;
- for(j=0;j<nmol;j++)
- if(mol == mol_id[j])
- bNew=FALSE;
-
- if(bNew)
- {
- mol_id[nmol]=mol;
- nmol++;
- }
-
- z=r[at][ZZ];
- if(z<zmin) zmin=z;
- if(z>zmax) zmax=z;
-
-/* index[count]=at;*/
- count++;
- }
- }
-
- mem_p->nmol=nmol;
- srenew(mol_id,nmol);
- mem_p->mol_id=mol_id;
-/* srenew(index,count);*/
-/* mem_p->mem_at.nr=count;*/
-/* sfree(mem_p->mem_at.index);*/
-/* mem_p->mem_at.index=index;*/
-
- if((zmax-zmin)>(box[ZZ][ZZ]-0.5))
- gmx_fatal(FARGS,"Something is wrong with your membrane. Max and min z values are %f and %f.\n"
- "Maybe your membrane is not centered in the box, but located at the box edge in the z-direction,\n"
- "so that one membrane is distributed over two periodic box images. Another possibility is that\n"
- "your water layer is not thick enough.\n",zmax,zmin);
- mem_p->zmin=zmin;
- mem_p->zmax=zmax;
- mem_p->zmed=(zmax-zmin)/2+zmin;
-
- /*number of membrane molecules in protein box*/
- nmolbox = count/mtop->molblock[block].natoms_mol;
- /*mem_area = box[XX][XX]*box[YY][YY]-box[XX][YY]*box[YY][XX];
- mem_p->lip_area = 2.0*mem_area/(double)mem_p->nmol;*/
- mem_area = (pos_ins->xmax[XX]-pos_ins->xmin[XX])*(pos_ins->xmax[YY]-pos_ins->xmin[YY]);
- mem_p->lip_area = 2.0*mem_area/(double)nmolbox;
-
- return mem_p->mem_at.nr;
-}
-
-void init_resize(t_block *ins_at,rvec *r_ins,pos_ins_t *pos_ins,mem_t *mem_p,rvec *r, gmx_bool bALLOW_ASYMMETRY)
-{
- int i,j,at,c,outsidesum,gctr=0;
- int idxsum=0;
-
- /*sanity check*/
- for (i=0;i<pos_ins->pieces;i++)
- idxsum+=pos_ins->nidx[i];
- if (idxsum!=ins_at->nr)
- gmx_fatal(FARGS,"Piecewise sum of inserted atoms not same as size of group selected to insert.");
-
- snew(pos_ins->geom_cent,pos_ins->pieces);
- for (i=0;i<pos_ins->pieces;i++)
- {
- c=0;
- outsidesum=0;
- for(j=0;j<DIM;j++)
- pos_ins->geom_cent[i][j]=0;
-
- for(j=0;j<DIM;j++)
- pos_ins->geom_cent[i][j]=0;
- for (j=0;j<pos_ins->nidx[i];j++)
- {
- at=pos_ins->subindex[i][j];
- copy_rvec(r[at],r_ins[gctr]);
- if( (r_ins[gctr][ZZ]<mem_p->zmax) && (r_ins[gctr][ZZ]>mem_p->zmin) )
- {
- rvec_inc(pos_ins->geom_cent[i],r_ins[gctr]);
- c++;
- }
- else
- outsidesum++;
- gctr++;
- }
- if (c>0)
- svmul(1/(double)c,pos_ins->geom_cent[i],pos_ins->geom_cent[i]);
- if (!bALLOW_ASYMMETRY)
- pos_ins->geom_cent[i][ZZ]=mem_p->zmed;
-
- fprintf(stderr,"Embedding piece %d with center of geometry: %f %f %f\n",i,pos_ins->geom_cent[i][XX],pos_ins->geom_cent[i][YY],pos_ins->geom_cent[i][ZZ]);
- }
- fprintf(stderr,"\n");
-}
-
-void resize(t_block *ins_at, rvec *r_ins, rvec *r, pos_ins_t *pos_ins,rvec fac)
-{
- int i,j,k,at,c=0;
- for (k=0;k<pos_ins->pieces;k++)
- for(i=0;i<pos_ins->nidx[k];i++)
- {
- at=pos_ins->subindex[k][i];
- for(j=0;j<DIM;j++)
- r[at][j]=pos_ins->geom_cent[k][j]+fac[j]*(r_ins[c][j]-pos_ins->geom_cent[k][j]);
- c++;
- }
-}
-
-int gen_rm_list(rm_t *rm_p,t_block *ins_at,t_block *rest_at,t_pbc *pbc, gmx_mtop_t *mtop,
- rvec *r, rvec *r_ins, mem_t *mem_p, pos_ins_t *pos_ins, real probe_rad, int low_up_rm, gmx_bool bALLOW_ASYMMETRY)
-{
- int i,j,k,l,at,at2,mol_id;
- int type=0,block=0;
- int nrm,nupper,nlower;
- real r_min_rad,z_lip,min_norm;
- gmx_bool bRM;
- rvec dr,dr_tmp;
- real *dist;
- int *order;
-
- r_min_rad=probe_rad*probe_rad;
- snew(rm_p->mol,mtop->mols.nr);
- snew(rm_p->block,mtop->mols.nr);
- nrm=nupper=0;
- nlower=low_up_rm;
- for(i=0;i<ins_at->nr;i++)
- {
- at=ins_at->index[i];
- for(j=0;j<rest_at->nr;j++)
- {
- at2=rest_at->index[j];
- pbc_dx(pbc,r[at],r[at2],dr);
-
- if(norm2(dr)<r_min_rad)
- {
- mol_id = get_mol_id(at2,mtop->nmolblock,mtop->molblock,&type,&block);
- bRM=TRUE;
- for(l=0;l<nrm;l++)
- if(rm_p->mol[l]==mol_id)
- bRM=FALSE;
- if(bRM)
- {
- /*fprintf(stderr,"%d wordt toegevoegd\n",mol_id);*/
- rm_p->mol[nrm]=mol_id;
- rm_p->block[nrm]=block;
- nrm++;
- z_lip=0.0;
- for(l=0;l<mem_p->nmol;l++)
- {
- if(mol_id==mem_p->mol_id[l])
- {
- for(k=mtop->mols.index[mol_id];k<mtop->mols.index[mol_id+1];k++)
- z_lip+=r[k][ZZ];
- z_lip/=mtop->molblock[block].natoms_mol;
- if(z_lip<mem_p->zmed)
- nlower++;
- else
- nupper++;
- }
- }
- }
- }
- }
- }
-
- /*make sure equal number of lipids from upper and lower layer are removed */
- if( (nupper!=nlower) && (!bALLOW_ASYMMETRY) )
- {
- snew(dist,mem_p->nmol);
- snew(order,mem_p->nmol);
- for(i=0;i<mem_p->nmol;i++)
- {
- at = mtop->mols.index[mem_p->mol_id[i]];
- pbc_dx(pbc,r[at],pos_ins->geom_cent[0],dr);
- if (pos_ins->pieces>1)
- {
- /*minimum dr value*/
- min_norm=norm2(dr);
- for (k=1;k<pos_ins->pieces;k++)
- {
- pbc_dx(pbc,r[at],pos_ins->geom_cent[k],dr_tmp);
- if (norm2(dr_tmp) < min_norm)
- {
- min_norm=norm2(dr_tmp);
- copy_rvec(dr_tmp,dr);
- }
- }
- }
- dist[i]=dr[XX]*dr[XX]+dr[YY]*dr[YY];
- j=i-1;
- while (j>=0 && dist[i]<dist[order[j]])
- {
- order[j+1]=order[j];
- j--;
- }
- order[j+1]=i;
- }
-
- i=0;
- while(nupper!=nlower)
- {
- mol_id=mem_p->mol_id[order[i]];
- block=get_block(mol_id,mtop->nmolblock,mtop->molblock);
-
- bRM=TRUE;
- for(l=0;l<nrm;l++)
- if(rm_p->mol[l]==mol_id)
- bRM=FALSE;
- if(bRM)
- {
- z_lip=0;
- for(k=mtop->mols.index[mol_id];k<mtop->mols.index[mol_id+1];k++)
- z_lip+=r[k][ZZ];
- z_lip/=mtop->molblock[block].natoms_mol;
- if(nupper>nlower && z_lip<mem_p->zmed)
- {
- rm_p->mol[nrm]=mol_id;
- rm_p->block[nrm]=block;
- nrm++;
- nlower++;
- }
- else if (nupper<nlower && z_lip>mem_p->zmed)
- {
- rm_p->mol[nrm]=mol_id;
- rm_p->block[nrm]=block;
- nrm++;
- nupper++;
- }
- }
- i++;
-
- if(i>mem_p->nmol)
- gmx_fatal(FARGS,"Trying to remove more lipid molecules than there are in the membrane");
- }
- sfree(dist);
- sfree(order);
- }
-
- rm_p->nr=nrm;
- srenew(rm_p->mol,nrm);
- srenew(rm_p->block,nrm);
-
- return nupper+nlower;
-}
-
-void rm_group(t_inputrec *ir, gmx_groups_t *groups, gmx_mtop_t *mtop, rm_t *rm_p, t_state *state, t_block *ins_at, pos_ins_t *pos_ins)
-{
- int i,j,k,n,rm,mol_id,at,block;
- rvec *x_tmp,*v_tmp;
- atom_id *list,*new_mols;
- unsigned char *new_egrp[egcNR];
- gmx_bool bRM;
-
- snew(list,state->natoms);
- n=0;
- for(i=0;i<rm_p->nr;i++)
- {
- mol_id=rm_p->mol[i];
- at=mtop->mols.index[mol_id];
- block =rm_p->block[i];
- mtop->molblock[block].nmol--;
- for(j=0;j<mtop->molblock[block].natoms_mol;j++)
- {
- list[n]=at+j;
- n++;
- }
-
- mtop->mols.index[mol_id]=-1;
- }
-
- mtop->mols.nr-=rm_p->nr;
- mtop->mols.nalloc_index-=rm_p->nr;
- snew(new_mols,mtop->mols.nr);
- for(i=0;i<mtop->mols.nr+rm_p->nr;i++)
- {
- j=0;
- if(mtop->mols.index[i]!=-1)
- {
- new_mols[j]=mtop->mols.index[i];
- j++;
- }
- }
- sfree(mtop->mols.index);
- mtop->mols.index=new_mols;
-
-
- mtop->natoms-=n;
- state->natoms-=n;
- state->nalloc=state->natoms;
- snew(x_tmp,state->nalloc);
- snew(v_tmp,state->nalloc);
-
- for(i=0;i<egcNR;i++)
- {
- if(groups->grpnr[i]!=NULL)
- {
- groups->ngrpnr[i]=state->natoms;
- snew(new_egrp[i],state->natoms);
- }
- }
-
- rm=0;
- for (i=0;i<state->natoms+n;i++)
- {
- bRM=FALSE;
- for(j=0;j<n;j++)
- {
- if(i==list[j])
- {
- bRM=TRUE;
- rm++;
- }
- }
-
- if(!bRM)
- {
- for(j=0;j<egcNR;j++)
- {
- if(groups->grpnr[j]!=NULL)
- {
- new_egrp[j][i-rm]=groups->grpnr[j][i];
- }
- }
- copy_rvec(state->x[i],x_tmp[i-rm]);
- copy_rvec(state->v[i],v_tmp[i-rm]);
- for(j=0;j<ins_at->nr;j++)
- {
- if (i==ins_at->index[j])
- ins_at->index[j]=i-rm;
- }
- for(j=0;j<pos_ins->pieces;j++)
- {
- for(k=0;k<pos_ins->nidx[j];k++)
- {
- if (i==pos_ins->subindex[j][k])
- pos_ins->subindex[j][k]=i-rm;
- }
- }
- }
- }
- sfree(state->x);
- state->x=x_tmp;
- sfree(state->v);
- state->v=v_tmp;
-
- for(i=0;i<egcNR;i++)
- {
- if(groups->grpnr[i]!=NULL)
- {
- sfree(groups->grpnr[i]);
- groups->grpnr[i]=new_egrp[i];
- }
- }
-}
-
-int rm_bonded(t_block *ins_at, gmx_mtop_t *mtop)
-{
- int i,j,m;
- int type,natom,nmol,at,atom1=0,rm_at=0;
- gmx_bool *bRM,bINS;
- /*this routine lives dangerously by assuming that all molecules of a given type are in order in the structure*/
- /*this routine does not live as dangerously as it seems. There is namely a check in mdrunner_membed to make
- *sure that g_membed exits with a warning when there are molecules of the same type not in the
- *ins_at index group. MGWolf 050710 */
-
-
- snew(bRM,mtop->nmoltype);
- for (i=0;i<mtop->nmoltype;i++)
- {
- bRM[i]=TRUE;
- }
-
- for (i=0;i<mtop->nmolblock;i++)
- {
- /*loop over molecule blocks*/
- type =mtop->molblock[i].type;
- natom =mtop->molblock[i].natoms_mol;
- nmol =mtop->molblock[i].nmol;
-
- for(j=0;j<natom*nmol && bRM[type]==TRUE;j++)
- {
- /*loop over atoms in the block*/
- at=j+atom1; /*atom index = block index + offset*/
- bINS=FALSE;
-
- for (m=0;(m<ins_at->nr) && (bINS==FALSE);m++)
- {
- /*loop over atoms in insertion index group to determine if we're inserting one*/
- if(at==ins_at->index[m])
- {
- bINS=TRUE;
- }
- }
- bRM[type]=bINS;
- }
- atom1+=natom*nmol; /*update offset*/
- if(bRM[type])
- {
- rm_at+=natom*nmol; /*increment bonded removal counter by # atoms in block*/
- }
- }
-
- for(i=0;i<mtop->nmoltype;i++)
- {
- if(bRM[i])
- {
- for(j=0;j<F_LJ;j++)
- {
- mtop->moltype[i].ilist[j].nr=0;
- }
- for(j=F_POSRES;j<=F_VSITEN;j++)
- {
- mtop->moltype[i].ilist[j].nr=0;
- }
- }
- }
- sfree(bRM);
-
- return rm_at;
-}
-
-void top_update(const char *topfile, char *ins, rm_t *rm_p, gmx_mtop_t *mtop)
-{
-#define TEMP_FILENM "temp.top"
- int bMolecules=0;
- FILE *fpin,*fpout;
- char buf[STRLEN],buf2[STRLEN],*temp;
- int i,*nmol_rm,nmol,line;
-
- fpin = ffopen(topfile,"r");
- fpout = ffopen(TEMP_FILENM,"w");
-
- snew(nmol_rm,mtop->nmoltype);
- for(i=0;i<rm_p->nr;i++)
- nmol_rm[rm_p->block[i]]++;
-
- line=0;
- while(fgets(buf,STRLEN,fpin))
- {
- line++;
- if(buf[0]!=';')
- {
- strcpy(buf2,buf);
- if ((temp=strchr(buf2,'\n')) != NULL)
- temp[0]='\0';
- ltrim(buf2);
-
- if (buf2[0]=='[')
- {
- buf2[0]=' ';
- if ((temp=strchr(buf2,'\n')) != NULL)
- temp[0]='\0';
- rtrim(buf2);
- if (buf2[strlen(buf2)-1]==']')
- {
- buf2[strlen(buf2)-1]='\0';
- ltrim(buf2);
- rtrim(buf2);
- if (gmx_strcasecmp(buf2,"molecules")==0)
- bMolecules=1;
- }
- fprintf(fpout,"%s",buf);
- } else if (bMolecules==1)
- {
- for(i=0;i<mtop->nmolblock;i++)
- {
- nmol=mtop->molblock[i].nmol;
- sprintf(buf,"%-15s %5d\n",*(mtop->moltype[mtop->molblock[i].type].name),nmol);
- fprintf(fpout,"%s",buf);
- }
- bMolecules=2;
- } else if (bMolecules==2)
- {
- /* print nothing */
- } else
- {
- fprintf(fpout,"%s",buf);
- }
- } else
- {
- fprintf(fpout,"%s",buf);
- }
- }
-
- fclose(fpout);
- /* use ffopen to generate backup of topinout */
- fpout=ffopen(topfile,"w");
- fclose(fpout);
- rename(TEMP_FILENM,topfile);
-#undef TEMP_FILENM
-}
-
-void md_print_warning(const t_commrec *cr,FILE *fplog,const char *buf)
-{
- if (MASTER(cr))
- {
- fprintf(stderr,"\n%s\n",buf);
- }
- if (fplog)
- {
- fprintf(fplog,"\n%s\n",buf);
- }
-}
-
-/* simulation conditions to transmit. Keep in mind that they are
- transmitted to other nodes through an MPI_Reduce after
- casting them to a real (so the signals can be sent together with other
- data). This means that the only meaningful values are positive,
- negative or zero. */
-enum { eglsNABNSB, eglsCHKPT, eglsSTOPCOND, eglsRESETCOUNTERS, eglsNR };
-/* Is the signal in one simulation independent of other simulations? */
-gmx_bool gs_simlocal[eglsNR] = { TRUE, FALSE, FALSE, TRUE };
-
-typedef struct {
- int nstms; /* The frequency for intersimulation communication */
- int sig[eglsNR]; /* The signal set by one process in do_md */
- int set[eglsNR]; /* The communicated signal, equal for all processes */
-} globsig_t;
-
-
-static int multisim_min(const gmx_multisim_t *ms,int nmin,int n)
-{
- int *buf;
- gmx_bool bPos,bEqual;
- int s,d;
-
- snew(buf,ms->nsim);
- buf[ms->sim] = n;
- gmx_sumi_sim(ms->nsim,buf,ms);
- bPos = TRUE;
- bEqual = TRUE;
- for(s=0; s<ms->nsim; s++)
- {
- bPos = bPos && (buf[s] > 0);
- bEqual = bEqual && (buf[s] == buf[0]);
- }
- if (bPos)
- {
- if (bEqual)
- {
- nmin = min(nmin,buf[0]);
- }
- else
- {
- /* Find the least common multiple */
- for(d=2; d<nmin; d++)
- {
- s = 0;
- while (s < ms->nsim && d % buf[s] == 0)
- {
- s++;
- }
- if (s == ms->nsim)
- {
- /* We found the LCM and it is less than nmin */
- nmin = d;
- break;
- }
- }
- }
- }
- sfree(buf);
-
- return nmin;
-}
-
-static int multisim_nstsimsync(const t_commrec *cr,
- const t_inputrec *ir,int repl_ex_nst)
-{
- int nmin;
-
- if (MASTER(cr))
- {
- nmin = INT_MAX;
- nmin = multisim_min(cr->ms,nmin,ir->nstlist);
- nmin = multisim_min(cr->ms,nmin,ir->nstcalcenergy);
- nmin = multisim_min(cr->ms,nmin,repl_ex_nst);
- if (nmin == INT_MAX)
- {
- gmx_fatal(FARGS,"Can not find an appropriate interval for inter-simulation communication, since nstlist, nstcalcenergy and -replex are all <= 0");
- }
- /* Avoid inter-simulation communication at every (second) step */
- if (nmin <= 2)
- {
- nmin = 10;
- }
- }
-
- gmx_bcast(sizeof(int),&nmin,cr);
-
- return nmin;
-}
-
-static void init_global_signals(globsig_t *gs,const t_commrec *cr,
- const t_inputrec *ir,int repl_ex_nst)
-{
- int i;
-
- if (MULTISIM(cr))
- {
- gs->nstms = multisim_nstsimsync(cr,ir,repl_ex_nst);
- if (debug)
- {
- fprintf(debug,"Syncing simulations for checkpointing and termination every %d steps\n",gs->nstms);
- }
- }
- else
- {
- gs->nstms = 1;
- }
-
- for(i=0; i<eglsNR; i++)
- {
- gs->sig[i] = 0;
- gs->set[i] = 0;
- }
-}
-
-static void copy_coupling_state(t_state *statea,t_state *stateb,
- gmx_ekindata_t *ekinda,gmx_ekindata_t *ekindb, t_grpopts* opts)
-{
-
- /* MRS note -- might be able to get rid of some of the arguments. Look over it when it's all debugged */
-
- int i,j,nc;
-
- /* Make sure we have enough space for x and v */
- if (statea->nalloc > stateb->nalloc)
- {
- stateb->nalloc = statea->nalloc;
- srenew(stateb->x,stateb->nalloc);
- srenew(stateb->v,stateb->nalloc);
- }
-
- stateb->natoms = statea->natoms;
- stateb->ngtc = statea->ngtc;
- stateb->nnhpres = statea->nnhpres;
- stateb->veta = statea->veta;
- if (ekinda)
- {
- copy_mat(ekinda->ekin,ekindb->ekin);
- for (i=0; i<stateb->ngtc; i++)
- {
- ekindb->tcstat[i].T = ekinda->tcstat[i].T;
- ekindb->tcstat[i].Th = ekinda->tcstat[i].Th;
- copy_mat(ekinda->tcstat[i].ekinh,ekindb->tcstat[i].ekinh);
- copy_mat(ekinda->tcstat[i].ekinf,ekindb->tcstat[i].ekinf);
- ekindb->tcstat[i].ekinscalef_nhc = ekinda->tcstat[i].ekinscalef_nhc;
- ekindb->tcstat[i].ekinscaleh_nhc = ekinda->tcstat[i].ekinscaleh_nhc;
- ekindb->tcstat[i].vscale_nhc = ekinda->tcstat[i].vscale_nhc;
- }
- }
- copy_rvecn(statea->x,stateb->x,0,stateb->natoms);
- copy_rvecn(statea->v,stateb->v,0,stateb->natoms);
- copy_mat(statea->box,stateb->box);
- copy_mat(statea->box_rel,stateb->box_rel);
- copy_mat(statea->boxv,stateb->boxv);
-
- for (i = 0; i<stateb->ngtc; i++)
- {
- nc = i*opts->nhchainlength;
- for (j=0; j<opts->nhchainlength; j++)
- {
- stateb->nosehoover_xi[nc+j] = statea->nosehoover_xi[nc+j];
- stateb->nosehoover_vxi[nc+j] = statea->nosehoover_vxi[nc+j];
- }
- }
- if (stateb->nhpres_xi != NULL)
- {
- for (i = 0; i<stateb->nnhpres; i++)
- {
- nc = i*opts->nhchainlength;
- for (j=0; j<opts->nhchainlength; j++)
- {
- stateb->nhpres_xi[nc+j] = statea->nhpres_xi[nc+j];
- stateb->nhpres_vxi[nc+j] = statea->nhpres_vxi[nc+j];
- }
- }
- }
-}
-
-static void compute_globals(FILE *fplog, gmx_global_stat_t gstat, t_commrec *cr, t_inputrec *ir,
- t_forcerec *fr, gmx_ekindata_t *ekind,
- t_state *state, t_state *state_global, t_mdatoms *mdatoms,
- t_nrnb *nrnb, t_vcm *vcm, gmx_wallcycle_t wcycle,
- gmx_enerdata_t *enerd,tensor force_vir, tensor shake_vir, tensor total_vir,
- tensor pres, rvec mu_tot, gmx_constr_t constr,
- globsig_t *gs,gmx_bool bInterSimGS,
- matrix box, gmx_mtop_t *top_global, real *pcurr,
- int natoms, gmx_bool *bSumEkinhOld, int flags)
-{
- int i,gsi;
- real gs_buf[eglsNR];
- tensor corr_vir,corr_pres;
- gmx_bool bEner,bPres,bTemp;
- gmx_bool bRerunMD, bStopCM, bGStat, bIterate,
- bFirstIterate,bReadEkin,bEkinAveVel,bScaleEkin, bConstrain;
- real prescorr,enercorr,dvdlcorr;
-
- /* translate CGLO flags to gmx_booleans */
- bRerunMD = flags & CGLO_RERUNMD;
- bStopCM = flags & CGLO_STOPCM;
- bGStat = flags & CGLO_GSTAT;
- bReadEkin = (flags & CGLO_READEKIN);
- bScaleEkin = (flags & CGLO_SCALEEKIN);
- bEner = flags & CGLO_ENERGY;
- bTemp = flags & CGLO_TEMPERATURE;
- bPres = (flags & CGLO_PRESSURE);
- bConstrain = (flags & CGLO_CONSTRAINT);
- bIterate = (flags & CGLO_ITERATE);
- bFirstIterate = (flags & CGLO_FIRSTITERATE);
-
- /* we calculate a full state kinetic energy either with full-step velocity verlet
- or half step where we need the pressure */
- bEkinAveVel = (ir->eI==eiVV || (ir->eI==eiVVAK && IR_NPT_TROTTER(ir) && bPres) || bReadEkin);
-
- /* in initalization, it sums the shake virial in vv, and to
- sums ekinh_old in leapfrog (or if we are calculating ekinh_old for other reasons */
-
- /* ########## Kinetic energy ############## */
-
- if (bTemp)
- {
- /* Non-equilibrium MD: this is parallellized, but only does communication
- * when there really is NEMD.
- */
-
- if (PAR(cr) && (ekind->bNEMD))
- {
- accumulate_u(cr,&(ir->opts),ekind);
- }
- debug_gmx();
- if (bReadEkin)
- {
- restore_ekinstate_from_state(cr,ekind,&state_global->ekinstate);
- }
- else
- {
-
- calc_ke_part(state,&(ir->opts),mdatoms,ekind,nrnb,bEkinAveVel,bIterate);
- }
-
- debug_gmx();
-
- /* Calculate center of mass velocity if necessary, also parallellized */
- if (bStopCM && !bRerunMD && bEner)
- {
- calc_vcm_grp(fplog,mdatoms->start,mdatoms->homenr,mdatoms,
- state->x,state->v,vcm);
- }
- }
-
- if (bTemp || bPres || bEner || bConstrain)
- {
- if (!bGStat)
- {
- /* We will not sum ekinh_old,
- * so signal that we still have to do it.
- */
- *bSumEkinhOld = TRUE;
-
- }
- else
- {
- if (gs != NULL)
- {
- for(i=0; i<eglsNR; i++)
- {
- gs_buf[i] = gs->sig[i];
- }
- }
- if (PAR(cr))
- {
- wallcycle_start(wcycle,ewcMoveE);
- GMX_MPE_LOG(ev_global_stat_start);
- global_stat(fplog,gstat,cr,enerd,force_vir,shake_vir,mu_tot,
- ir,ekind,constr,vcm,
- gs != NULL ? eglsNR : 0,gs_buf,
- top_global,state,
- *bSumEkinhOld,flags);
- GMX_MPE_LOG(ev_global_stat_finish);
- wallcycle_stop(wcycle,ewcMoveE);
- }
- if (gs != NULL)
- {
- if (MULTISIM(cr) && bInterSimGS)
- {
- if (MASTER(cr))
- {
- /* Communicate the signals between the simulations */
- gmx_sum_sim(eglsNR,gs_buf,cr->ms);
- }
- /* Communicate the signals form the master to the others */
- gmx_bcast(eglsNR*sizeof(gs_buf[0]),gs_buf,cr);
- }
- for(i=0; i<eglsNR; i++)
- {
- if (bInterSimGS || gs_simlocal[i])
- {
- /* Set the communicated signal only when it is non-zero,
- * since signals might not be processed at each MD step.
- */
- gsi = (gs_buf[i] >= 0 ?
- (int)(gs_buf[i] + 0.5) :
- (int)(gs_buf[i] - 0.5));
- if (gsi != 0)
- {
- gs->set[i] = gsi;
- }
- /* Turn off the local signal */
- gs->sig[i] = 0;
- }
- }
- }
- *bSumEkinhOld = FALSE;
- }
- }
-
- if (!ekind->bNEMD && debug && bTemp && (vcm->nr > 0))
- {
- correct_ekin(debug,
- mdatoms->start,mdatoms->start+mdatoms->homenr,
- state->v,vcm->group_p[0],
- mdatoms->massT,mdatoms->tmass,ekind->ekin);
- }
-
- if (bEner) {
- /* Do center of mass motion removal */
- if (bStopCM && !bRerunMD) /* is this correct? Does it get called too often with this logic? */
- {
- check_cm_grp(fplog,vcm,ir,1);
- do_stopcm_grp(fplog,mdatoms->start,mdatoms->homenr,mdatoms->cVCM,
- state->x,state->v,vcm);
- inc_nrnb(nrnb,eNR_STOPCM,mdatoms->homenr);
- }
- }
-
- if (bTemp)
- {
- /* Sum the kinetic energies of the groups & calc temp */
- /* compute full step kinetic energies if vv, or if vv-avek and we are computing the pressure with IR_NPT_TROTTER */
- /* three maincase: VV with AveVel (md-vv), vv with AveEkin (md-vv-avek), leap with AveEkin (md).
- Leap with AveVel is also an option for the future but not supported now.
- bEkinAveVel: If TRUE, we simply multiply ekin by ekinscale to get a full step kinetic energy.
- If FALSE, we average ekinh_old and ekinh*ekinscale_nhc to get an averaged half step kinetic energy.
- bSaveEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't reset the ekinscale_nhc.
- If FALSE, we go ahead and erase over it.
- */
- enerd->term[F_TEMP] = sum_ekin(&(ir->opts),ekind,&(enerd->term[F_DKDL]),
- bEkinAveVel,bIterate,bScaleEkin);
-
- enerd->term[F_EKIN] = trace(ekind->ekin);
- }
-
- /* ########## Long range energy information ###### */
-
- if (bEner || bPres || bConstrain)
- {
- calc_dispcorr(fplog,ir,fr,0,top_global->natoms,box,state->lambda,
- corr_pres,corr_vir,&prescorr,&enercorr,&dvdlcorr);
- }
-
- if (bEner && bFirstIterate)
- {
- enerd->term[F_DISPCORR] = enercorr;
- enerd->term[F_EPOT] += enercorr;
- enerd->term[F_DVDL] += dvdlcorr;
- if (fr->efep != efepNO) {
- enerd->dvdl_lin += dvdlcorr;
- }
- }
-
- /* ########## Now pressure ############## */
- if (bPres || bConstrain)
- {
-
- m_add(force_vir,shake_vir,total_vir);
-
- /* Calculate pressure and apply LR correction if PPPM is used.
- * Use the box from last timestep since we already called update().
- */
-
- enerd->term[F_PRES] = calc_pres(fr->ePBC,ir->nwall,box,ekind->ekin,total_vir,pres,
- (fr->eeltype==eelPPPM)?enerd->term[F_COUL_RECIP]:0.0);
-
- /* Calculate long range corrections to pressure and energy */
- /* this adds to enerd->term[F_PRES] and enerd->term[F_ETOT],
- and computes enerd->term[F_DISPCORR]. Also modifies the
- total_vir and pres tesors */
-
- m_add(total_vir,corr_vir,total_vir);
- m_add(pres,corr_pres,pres);
- enerd->term[F_PDISPCORR] = prescorr;
- enerd->term[F_PRES] += prescorr;
- *pcurr = enerd->term[F_PRES];
- /* calculate temperature using virial */
- enerd->term[F_VTEMP] = calc_temp(trace(total_vir),ir->opts.nrdf[0]);
-
- }
-}
-
-
-/* Definitions for convergence of iterated constraints */
-
-/* iterate constraints up to 50 times */
-#define MAXITERCONST 50
-
-/* data type */
-typedef struct
-{
- real f,fprev,x,xprev;
- int iter_i;
- gmx_bool bIterate;
- real allrelerr[MAXITERCONST+2];
- int num_close; /* number of "close" violations, caused by limited precision. */
-} gmx_iterate_t;
-
-#ifdef GMX_DOUBLE
-#define CONVERGEITER 0.000000001
-#define CLOSE_ENOUGH 0.000001000
-#else
-#define CONVERGEITER 0.0001
-#define CLOSE_ENOUGH 0.0050
-#endif
-
-/* we want to keep track of the close calls. If there are too many, there might be some other issues.
- so we make sure that it's either less than some predetermined number, or if more than that number,
- only some small fraction of the total. */
-#define MAX_NUMBER_CLOSE 50
-#define FRACTION_CLOSE 0.001
-
-/* maximum length of cyclic traps to check, emerging from limited numerical precision */
-#define CYCLEMAX 20
-
-static void gmx_iterate_init(gmx_iterate_t *iterate,gmx_bool bIterate)
-{
- int i;
-
- iterate->iter_i = 0;
- iterate->bIterate = bIterate;
- iterate->num_close = 0;
- for (i=0;i<MAXITERCONST+2;i++)
- {
- iterate->allrelerr[i] = 0;
- }
-}
-
-static gmx_bool done_iterating(const t_commrec *cr,FILE *fplog, int nsteps, gmx_iterate_t *iterate, gmx_bool bFirstIterate, real fom, real *newf)
-{
- /* monitor convergence, and use a secant search to propose new
- values.
- x_{i} - x_{i-1}
- The secant method computes x_{i+1} = x_{i} - f(x_{i}) * ---------------------
- f(x_{i}) - f(x_{i-1})
-
- The function we are trying to zero is fom-x, where fom is the
- "figure of merit" which is the pressure (or the veta value) we
- would get by putting in an old value of the pressure or veta into
- the incrementor function for the step or half step. I have
- verified that this gives the same answer as self consistent
- iteration, usually in many fewer steps, especially for small tau_p.
-
- We could possibly eliminate an iteration with proper use
- of the value from the previous step, but that would take a bit
- more bookkeeping, especially for veta, since tests indicate the
- function of veta on the last step is not sufficiently close to
- guarantee convergence this step. This is
- good enough for now. On my tests, I could use tau_p down to
- 0.02, which is smaller that would ever be necessary in
- practice. Generally, 3-5 iterations will be sufficient */
-
- real relerr,err;
- char buf[256];
- int i;
- gmx_bool incycle;
-
- if (bFirstIterate)
- {
- iterate->x = fom;
- iterate->f = fom-iterate->x;
- iterate->xprev = 0;
- iterate->fprev = 0;
- *newf = fom;
- }
- else
- {
- iterate->f = fom-iterate->x; /* we want to zero this difference */
- if ((iterate->iter_i > 1) && (iterate->iter_i < MAXITERCONST))
- {
- if (iterate->f==iterate->fprev)
- {
- *newf = iterate->f;
- }
- else
- {
- *newf = iterate->x - (iterate->x-iterate->xprev)*(iterate->f)/(iterate->f-iterate->fprev);
- }
- }
- else
- {
- /* just use self-consistent iteration the first step to initialize, or
- if it's not converging (which happens occasionally -- need to investigate why) */
- *newf = fom;
- }
- }
- /* Consider a slight shortcut allowing us to exit one sooner -- we check the
- difference between the closest of x and xprev to the new
- value. To be 100% certain, we should check the difference between
- the last result, and the previous result, or
-
- relerr = (fabs((x-xprev)/fom));
-
- but this is pretty much never necessary under typical conditions.
- Checking numerically, it seems to lead to almost exactly the same
- trajectories, but there are small differences out a few decimal
- places in the pressure, and eventually in the v_eta, but it could
- save an interation.
-
- if (fabs(*newf-x) < fabs(*newf - xprev)) { xmin = x;} else { xmin = xprev;}
- relerr = (fabs((*newf-xmin) / *newf));
- */
-
- err = fabs((iterate->f-iterate->fprev));
- relerr = fabs(err/fom);
-
- iterate->allrelerr[iterate->iter_i] = relerr;
-
- if (iterate->iter_i > 0)
- {
- if (debug)
- {
- fprintf(debug,"Iterating NPT constraints: %6i %20.12f%14.6g%20.12f\n",
- iterate->iter_i,fom,relerr,*newf);
- }
-
- if ((relerr < CONVERGEITER) || (err < CONVERGEITER) || (fom==0) || ((iterate->x == iterate->xprev) && iterate->iter_i > 1))
- {
- iterate->bIterate = FALSE;
- if (debug)
- {
- fprintf(debug,"Iterating NPT constraints: CONVERGED\n");
- }
- return TRUE;
- }
- if (iterate->iter_i > MAXITERCONST)
- {
- if (relerr < CLOSE_ENOUGH)
- {
- incycle = FALSE;
- for (i=1;i<CYCLEMAX;i++) {
- if ((iterate->allrelerr[iterate->iter_i-(1+i)] == iterate->allrelerr[iterate->iter_i-1]) &&
- (iterate->allrelerr[iterate->iter_i-(1+i)] == iterate->allrelerr[iterate->iter_i-(1+2*i)])) {
- incycle = TRUE;
- if (debug)
- {
- fprintf(debug,"Exiting from an NPT iterating cycle of length %d\n",i);
- }
- break;
- }
- }
-
- if (incycle) {
- /* step 1: trapped in a numerical attractor */
- /* we are trapped in a numerical attractor, and can't converge any more, and are close to the final result.
- Better to give up convergence here than have the simulation die.
- */
- iterate->num_close++;
- return TRUE;
- }
- else
- {
- /* Step #2: test if we are reasonably close for other reasons, then monitor the number. If not, die */
-
- /* how many close calls have we had? If less than a few, we're OK */
- if (iterate->num_close < MAX_NUMBER_CLOSE)
- {
- sprintf(buf,"Slight numerical convergence deviation with NPT at step %d, relative error only %10.5g, likely not a problem, continuing\n",nsteps,relerr);
- md_print_warning(cr,fplog,buf);
- iterate->num_close++;
- return TRUE;
- /* if more than a few, check the total fraction. If too high, die. */
- } else if (iterate->num_close/(double)nsteps > FRACTION_CLOSE) {
- gmx_fatal(FARGS,"Could not converge NPT constraints, too many exceptions (%d%%\n",iterate->num_close/(double)nsteps);
- }
- }
- }
- else
- {
- gmx_fatal(FARGS,"Could not converge NPT constraints\n");
- }
- }
- }
-
- iterate->xprev = iterate->x;
- iterate->x = *newf;
- iterate->fprev = iterate->f;
- iterate->iter_i++;
-
- return FALSE;
-}
-
-static void check_nst_param(FILE *fplog,t_commrec *cr,
- const char *desc_nst,int nst,
- const char *desc_p,int *p)
-{
- char buf[STRLEN];
-
- if (*p > 0 && *p % nst != 0)
- {
- /* Round up to the next multiple of nst */
- *p = ((*p)/nst + 1)*nst;
- sprintf(buf,"NOTE: %s changes %s to %d\n",desc_nst,desc_p,*p);
- md_print_warning(cr,fplog,buf);
- }
-}
-
-static void reset_all_counters(FILE *fplog,t_commrec *cr,
- gmx_large_int_t step,
- gmx_large_int_t *step_rel,t_inputrec *ir,
- gmx_wallcycle_t wcycle,t_nrnb *nrnb,
- gmx_runtime_t *runtime)
-{
- char buf[STRLEN],sbuf[STEPSTRSIZE];
-
- /* Reset all the counters related to performance over the run */
- sprintf(buf,"Step %s: resetting all time and cycle counters\n",
- gmx_step_str(step,sbuf));
- md_print_warning(cr,fplog,buf);
-
- wallcycle_stop(wcycle,ewcRUN);
- wallcycle_reset_all(wcycle);
- if (DOMAINDECOMP(cr))
- {
- reset_dd_statistics_counters(cr->dd);
- }
- init_nrnb(nrnb);
- ir->init_step += *step_rel;
- ir->nsteps -= *step_rel;
- *step_rel = 0;
- wallcycle_start(wcycle,ewcRUN);
- runtime_start(runtime);
- print_date_and_time(fplog,cr->nodeid,"Restarted time",runtime);
-}
-
-static int check_nstglobalcomm(FILE *fplog,t_commrec *cr,
- int nstglobalcomm,t_inputrec *ir)
-{
- char buf[STRLEN];
-
- if (!EI_DYNAMICS(ir->eI))
- {
- nstglobalcomm = 1;
- }
-
- if (nstglobalcomm == -1)
- {
- if (ir->nstcalcenergy == 0 && ir->nstlist == 0)
- {
- nstglobalcomm = 10;
- if (ir->nstenergy > 0 && ir->nstenergy < nstglobalcomm)
- {
- nstglobalcomm = ir->nstenergy;
- }
- }
- else
- {
- /* We assume that if nstcalcenergy > nstlist,
- * nstcalcenergy is a multiple of nstlist.
- */
- if (ir->nstcalcenergy == 0 ||
- (ir->nstlist > 0 && ir->nstlist < ir->nstcalcenergy))
- {
- nstglobalcomm = ir->nstlist;
- }
- else
- {
- nstglobalcomm = ir->nstcalcenergy;
- }
- }
- }
- else
- {
- if (ir->nstlist > 0 &&
- nstglobalcomm > ir->nstlist && nstglobalcomm % ir->nstlist != 0)
- {
- nstglobalcomm = (nstglobalcomm / ir->nstlist)*ir->nstlist;
- sprintf(buf,"WARNING: nstglobalcomm is larger than nstlist, but not a multiple, setting it to %d\n",nstglobalcomm);
- md_print_warning(cr,fplog,buf);
- }
- if (nstglobalcomm > ir->nstcalcenergy)
- {
- check_nst_param(fplog,cr,"-gcom",nstglobalcomm,
- "nstcalcenergy",&ir->nstcalcenergy);
- }
-
- check_nst_param(fplog,cr,"-gcom",nstglobalcomm,
- "nstenergy",&ir->nstenergy);
-
- check_nst_param(fplog,cr,"-gcom",nstglobalcomm,
- "nstlog",&ir->nstlog);
- }
-
- if (ir->comm_mode != ecmNO && ir->nstcomm < nstglobalcomm)
- {
- sprintf(buf,"WARNING: Changing nstcomm from %d to %d\n",
- ir->nstcomm,nstglobalcomm);
- md_print_warning(cr,fplog,buf);
- ir->nstcomm = nstglobalcomm;
- }
-
- return nstglobalcomm;
-}
-
-void check_ir_old_tpx_versions(t_commrec *cr,FILE *fplog,
- t_inputrec *ir,gmx_mtop_t *mtop)
-{
- /* Check required for old tpx files */
- if (IR_TWINRANGE(*ir) && ir->nstlist > 1 &&
- ir->nstcalcenergy % ir->nstlist != 0)
- {
- md_print_warning(cr,fplog,"Old tpr file with twin-range settings: modifying energy calculation and/or T/P-coupling frequencies");
-
- if (gmx_mtop_ftype_count(mtop,F_CONSTR) +
- gmx_mtop_ftype_count(mtop,F_CONSTRNC) > 0 &&
- ir->eConstrAlg == econtSHAKE)
- {
- md_print_warning(cr,fplog,"With twin-range cut-off's and SHAKE the virial and pressure are incorrect");
- if (ir->epc != epcNO)
- {
- gmx_fatal(FARGS,"Can not do pressure coupling with twin-range cut-off's and SHAKE");
- }
- }
- check_nst_param(fplog,cr,"nstlist",ir->nstlist,
- "nstcalcenergy",&ir->nstcalcenergy);
- check_nst_param(fplog,cr,"nstcalcenergy",ir->nstcalcenergy,
- "nstenergy",&ir->nstenergy);
- check_nst_param(fplog,cr,"nstcalcenergy",ir->nstcalcenergy,
- "nstlog",&ir->nstlog);
- if (ir->efep != efepNO)
- {
- check_nst_param(fplog,cr,"nstcalcenergy",ir->nstcalcenergy,
- "nstdhdl",&ir->nstdhdl);
- }
- }
-}
-
-typedef struct {
- gmx_bool bGStatEveryStep;
- gmx_large_int_t step_ns;
- gmx_large_int_t step_nscheck;
- gmx_large_int_t nns;
- matrix scale_tot;
- int nabnsb;
- double s1;
- double s2;
- double ab;
- double lt_runav;
- double lt_runav2;
-} gmx_nlheur_t;
-
-static void reset_nlistheuristics(gmx_nlheur_t *nlh,gmx_large_int_t step)
-{
- nlh->lt_runav = 0;
- nlh->lt_runav2 = 0;
- nlh->step_nscheck = step;
-}
-
-static void init_nlistheuristics(gmx_nlheur_t *nlh,
- gmx_bool bGStatEveryStep,gmx_large_int_t step)
-{
- nlh->bGStatEveryStep = bGStatEveryStep;
- nlh->nns = 0;
- nlh->nabnsb = 0;
- nlh->s1 = 0;
- nlh->s2 = 0;
- nlh->ab = 0;
-
- reset_nlistheuristics(nlh,step);
-}
-
-static void update_nliststatistics(gmx_nlheur_t *nlh,gmx_large_int_t step)
-{
- gmx_large_int_t nl_lt;
- char sbuf[STEPSTRSIZE],sbuf2[STEPSTRSIZE];
-
- /* Determine the neighbor list life time */
- nl_lt = step - nlh->step_ns;
- if (debug)
- {
- fprintf(debug,"%d atoms beyond ns buffer, updating neighbor list after %s steps\n",nlh->nabnsb,gmx_step_str(nl_lt,sbuf));
- }
- nlh->nns++;
- nlh->s1 += nl_lt;
- nlh->s2 += nl_lt*nl_lt;
- nlh->ab += nlh->nabnsb;
- if (nlh->lt_runav == 0)
- {
- nlh->lt_runav = nl_lt;
- /* Initialize the fluctuation average
- * such that at startup we check after 0 steps.
- */
- nlh->lt_runav2 = sqr(nl_lt/2.0);
- }
- /* Running average with 0.9 gives an exp. history of 9.5 */
- nlh->lt_runav2 = 0.9*nlh->lt_runav2 + 0.1*sqr(nlh->lt_runav - nl_lt);
- nlh->lt_runav = 0.9*nlh->lt_runav + 0.1*nl_lt;
- if (nlh->bGStatEveryStep)
- {
- /* Always check the nlist validity */
- nlh->step_nscheck = step;
- }
- else
- {
- /* We check after: <life time> - 2*sigma
- * The factor 2 is quite conservative,
- * but we assume that with nstlist=-1 the user
- * prefers exact integration over performance.
- */
- nlh->step_nscheck = step
- + (int)(nlh->lt_runav - 2.0*sqrt(nlh->lt_runav2)) - 1;
- }
- if (debug)
- {
- fprintf(debug,"nlist life time %s run av. %4.1f sig %3.1f check %s check with -gcom %d\n",
- gmx_step_str(nl_lt,sbuf),nlh->lt_runav,sqrt(nlh->lt_runav2),
- gmx_step_str(nlh->step_nscheck-step+1,sbuf2),
- (int)(nlh->lt_runav - 2.0*sqrt(nlh->lt_runav2)));
- }
-}
-
-static void set_nlistheuristics(gmx_nlheur_t *nlh,gmx_bool bReset,gmx_large_int_t step)
-{
- int d;
-
- if (bReset)
- {
- reset_nlistheuristics(nlh,step);
- }
- else
- {
- update_nliststatistics(nlh,step);
- }
-
- nlh->step_ns = step;
- /* Initialize the cumulative coordinate scaling matrix */
- clear_mat(nlh->scale_tot);
- for(d=0; d<DIM; d++)
- {
- nlh->scale_tot[d][d] = 1.0;
- }
-}
-
-double do_md_membed(FILE *fplog,t_commrec *cr,int nfile,const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- gmx_vsite_t *vsite,gmx_constr_t constr,
- int stepout,t_inputrec *ir,
- gmx_mtop_t *top_global,
- t_fcdata *fcd,
- t_state *state_global,
- t_mdatoms *mdatoms,
- t_nrnb *nrnb,gmx_wallcycle_t wcycle,
- gmx_edsam_t ed,t_forcerec *fr,
- int repl_ex_nst,int repl_ex_seed,
- real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- gmx_runtime_t *runtime,
- rvec fac, rvec *r_ins, pos_ins_t *pos_ins, t_block *ins_at,
- real xy_step, real z_step, int it_xy, int it_z)
-{
- gmx_mdoutf_t *outf;
- gmx_large_int_t step,step_rel;
- double run_time;
- double t,t0,lam0;
- gmx_bool bGStatEveryStep,bGStat,bNstEner,bCalcEnerPres;
- gmx_bool bNS,bNStList,bSimAnn,bStopCM,bRerunMD,bNotLastFrame=FALSE,
- bFirstStep,bStateFromTPX,bInitStep,bLastStep,
- bBornRadii,bStartingFromCpt;
- gmx_bool bDoDHDL=FALSE;
- gmx_bool do_ene,do_log,do_verbose,bRerunWarnNoV=TRUE,
- bForceUpdate=FALSE,bCPT;
- int mdof_flags;
- gmx_bool bMasterState;
- int force_flags,cglo_flags;
- tensor force_vir,shake_vir,total_vir,tmp_vir,pres;
- int i,m;
- t_trxstatus *status;
- rvec mu_tot;
- t_vcm *vcm;
- t_state *bufstate=NULL;
- matrix *scale_tot,pcoupl_mu,M,ebox;
- gmx_nlheur_t nlh;
- t_trxframe rerun_fr;
-/* gmx_repl_ex_t repl_ex=NULL;*/
- int nchkpt=1;
-
- gmx_localtop_t *top;
- t_mdebin *mdebin=NULL;
- t_state *state=NULL;
- rvec *f_global=NULL;
- int n_xtc=-1;
- rvec *x_xtc=NULL;
- gmx_enerdata_t *enerd;
- rvec *f=NULL;
- gmx_global_stat_t gstat;
- gmx_update_t upd=NULL;
- t_graph *graph=NULL;
- globsig_t gs;
-
- gmx_bool bFFscan;
- gmx_groups_t *groups;
- gmx_ekindata_t *ekind, *ekind_save;
- gmx_shellfc_t shellfc;
- int count,nconverged=0;
- real timestep=0;
- double tcount=0;
- gmx_bool bIonize=FALSE;
- gmx_bool bTCR=FALSE,bConverged=TRUE,bOK,bSumEkinhOld,bExchanged;
- gmx_bool bAppend;
- gmx_bool bResetCountersHalfMaxH=FALSE;
- gmx_bool bVV,bIterations,bIterate,bFirstIterate,bTemp,bPres,bTrotter;
- real temp0,dvdl;
- int a0,a1,ii;
- rvec *xcopy=NULL,*vcopy=NULL,*cbuf=NULL;
- matrix boxcopy={{0}},lastbox;
- real veta_save,pcurr,scalevir,tracevir;
- real vetanew = 0;
- double cycles;
- real last_conserved = 0;
- real last_ekin = 0;
- t_extmass MassQ;
- int **trotter_seq;
- char sbuf[STEPSTRSIZE],sbuf2[STEPSTRSIZE];
- int handled_stop_condition=gmx_stop_cond_none; /* compare to get_stop_condition*/
- gmx_iterate_t iterate;
-#ifdef GMX_FAHCORE
- /* Temporary addition for FAHCORE checkpointing */
- int chkpt_ret;
-#endif
-
- /* Check for special mdrun options */
- bRerunMD = (Flags & MD_RERUN);
- bIonize = (Flags & MD_IONIZE);
- bFFscan = (Flags & MD_FFSCAN);
- bAppend = (Flags & MD_APPENDFILES);
- bGStatEveryStep = FALSE;
- if (Flags & MD_RESETCOUNTERSHALFWAY)
- {
- if (ir->nsteps > 0)
- {
- /* Signal to reset the counters half the simulation steps. */
- wcycle_set_reset_counters(wcycle,ir->nsteps/2);
- }
- /* Signal to reset the counters halfway the simulation time. */
- bResetCountersHalfMaxH = (max_hours > 0);
- }
-
- /* md-vv uses averaged full step velocities for T-control
- md-vv-avek uses averaged half step velocities for T-control (but full step ekin for P control)
- md uses averaged half step kinetic energies to determine temperature unless defined otherwise by GMX_EKIN_AVE_VEL; */
- bVV = EI_VV(ir->eI);
- if (bVV) /* to store the initial velocities while computing virial */
- {
- snew(cbuf,top_global->natoms);
- }
- /* all the iteratative cases - only if there are constraints */
- bIterations = ((IR_NPT_TROTTER(ir)) && (constr) && (!bRerunMD));
- bTrotter = (bVV && (IR_NPT_TROTTER(ir) || (IR_NVT_TROTTER(ir))));
-
- if (bRerunMD)
- {
- /* Since we don't know if the frames read are related in any way,
- * rebuild the neighborlist at every step.
- */
- ir->nstlist = 1;
- ir->nstcalcenergy = 1;
- nstglobalcomm = 1;
- }
-
- check_ir_old_tpx_versions(cr,fplog,ir,top_global);
-
- nstglobalcomm = check_nstglobalcomm(fplog,cr,nstglobalcomm,ir);
- /*bGStatEveryStep = (nstglobalcomm == 1);*/
- bGStatEveryStep = FALSE;
-
- if (!bGStatEveryStep && ir->nstlist == -1 && fplog != NULL)
- {
- fprintf(fplog,
- "To reduce the energy communication with nstlist = -1\n"
- "the neighbor list validity should not be checked at every step,\n"
- "this means that exact integration is not guaranteed.\n"
- "The neighbor list validity is checked after:\n"
- " <n.list life time> - 2*std.dev.(n.list life time) steps.\n"
- "In most cases this will result in exact integration.\n"
- "This reduces the energy communication by a factor of 2 to 3.\n"
- "If you want less energy communication, set nstlist > 3.\n\n");
- }
-
- if (bRerunMD || bFFscan)
- {
- ir->nstxtcout = 0;
- }
- groups = &top_global->groups;
-
- /* Initial values */
- init_md(fplog,cr,ir,oenv,&t,&t0,&state_global->lambda,&lam0,
- nrnb,top_global,&upd,
- nfile,fnm,&outf,&mdebin,
- force_vir,shake_vir,mu_tot,&bSimAnn,&vcm,state_global,Flags);
-
- clear_mat(total_vir);
- clear_mat(pres);
- /* Energy terms and groups */
- snew(enerd,1);
- init_enerdata(top_global->groups.grps[egcENER].nr,ir->n_flambda,enerd);
- if (DOMAINDECOMP(cr))
- {
- f = NULL;
- }
- else
- {
- snew(f,top_global->natoms);
- }
-
- /* Kinetic energy data */
- snew(ekind,1);
- init_ekindata(fplog,top_global,&(ir->opts),ekind);
- /* needed for iteration of constraints */
- snew(ekind_save,1);
- init_ekindata(fplog,top_global,&(ir->opts),ekind_save);
- /* Copy the cos acceleration to the groups struct */
- ekind->cosacc.cos_accel = ir->cos_accel;
-
- gstat = global_stat_init(ir);
- debug_gmx();
-
- /* Check for polarizable models and flexible constraints */
- shellfc = init_shell_flexcon(fplog,
- top_global,n_flexible_constraints(constr),
- (ir->bContinuation ||
- (DOMAINDECOMP(cr) && !MASTER(cr))) ?
- NULL : state_global->x);
-
-/* if (DEFORM(*ir))
- {
-#ifdef GMX_THREADS
- tMPI_Thread_mutex_lock(&deform_init_box_mutex);
-#endif
- set_deform_reference_box(upd,
- deform_init_init_step_tpx,
- deform_init_box_tpx);
-#ifdef GMX_THREADS
- tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
-#endif
- }*/
-
-/* {
- double io = compute_io(ir,top_global->natoms,groups,mdebin->ebin->nener,1);
- if ((io > 2000) && MASTER(cr))
- fprintf(stderr,
- "\nWARNING: This run will generate roughly %.0f Mb of data\n\n",
- io);
- }*/
-
- if (DOMAINDECOMP(cr)) {
- top = dd_init_local_top(top_global);
-
- snew(state,1);
- dd_init_local_state(cr->dd,state_global,state);
-
- if (DDMASTER(cr->dd) && ir->nstfout) {
- snew(f_global,state_global->natoms);
- }
- } else {
- if (PAR(cr)) {
- /* Initialize the particle decomposition and split the topology */
- top = split_system(fplog,top_global,ir,cr);
-
- pd_cg_range(cr,&fr->cg0,&fr->hcg);
- pd_at_range(cr,&a0,&a1);
- } else {
- top = gmx_mtop_generate_local_top(top_global,ir);
-
- a0 = 0;
- a1 = top_global->natoms;
- }
-
- state = partdec_init_local_state(cr,state_global);
- f_global = f;
-
- atoms2md(top_global,ir,0,NULL,a0,a1-a0,mdatoms);
-
- if (vsite) {
- set_vsite_top(vsite,top,mdatoms,cr);
- }
-
- if (ir->ePBC != epbcNONE && !ir->bPeriodicMols) {
- graph = mk_graph(fplog,&(top->idef),0,top_global->natoms,FALSE,FALSE);
- }
-
- if (shellfc) {
- make_local_shells(cr,mdatoms,shellfc);
- }
-
- if (ir->pull && PAR(cr)) {
- dd_make_local_pull_groups(NULL,ir->pull,mdatoms);
- }
- }
-
- if (DOMAINDECOMP(cr))
- {
- /* Distribute the charge groups over the nodes from the master node */
- dd_partition_system(fplog,ir->init_step,cr,TRUE,1,
- state_global,top_global,ir,
- state,&f,mdatoms,top,fr,
- vsite,shellfc,constr,
- nrnb,wcycle,FALSE);
- }
-
- update_mdatoms(mdatoms,state->lambda);
-
- if (MASTER(cr))
- {
- if (opt2bSet("-cpi",nfile,fnm))
- {
- /* Update mdebin with energy history if appending to output files */
- if ( Flags & MD_APPENDFILES )
- {
- restore_energyhistory_from_state(mdebin,&state_global->enerhist);
- }
- else
- {
- /* We might have read an energy history from checkpoint,
- * free the allocated memory and reset the counts.
- */
- done_energyhistory(&state_global->enerhist);
- init_energyhistory(&state_global->enerhist);
- }
- }
- /* Set the initial energy history in state by updating once */
- update_energyhistory(&state_global->enerhist,mdebin);
- }
-
- if ((state->flags & (1<<estLD_RNG)) && (Flags & MD_READ_RNG)) {
- /* Set the random state if we read a checkpoint file */
- set_stochd_state(upd,state);
- }
-
- /* Initialize constraints */
- if (constr) {
- if (!DOMAINDECOMP(cr))
- set_constraints(constr,top,ir,mdatoms,cr);
- }
-
- /* Check whether we have to GCT stuff */
- /* bTCR = ftp2bSet(efGCT,nfile,fnm);
- if (bTCR) {
- if (MASTER(cr)) {
- fprintf(stderr,"Will do General Coupling Theory!\n");
- }
- gnx = top_global->mols.nr;
- snew(grpindex,gnx);
- for(i=0; (i<gnx); i++) {
- grpindex[i] = i;
- }
- }*/
-
-/* if (repl_ex_nst > 0 && MASTER(cr))
- repl_ex = init_replica_exchange(fplog,cr->ms,state_global,ir,
- repl_ex_nst,repl_ex_seed);*/
-
- if (!ir->bContinuation && !bRerunMD)
- {
- if (mdatoms->cFREEZE && (state->flags & (1<<estV)))
- {
- /* Set the velocities of frozen particles to zero */
- for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++)
- {
- for(m=0; m<DIM; m++)
- {
- if (ir->opts.nFreeze[mdatoms->cFREEZE[i]][m])
- {
- state->v[i][m] = 0;
- }
- }
- }
- }
-
- if (constr)
- {
- /* Constrain the initial coordinates and velocities */
- do_constrain_first(fplog,constr,ir,mdatoms,state,f,
- graph,cr,nrnb,fr,top,shake_vir);
- }
- if (vsite)
- {
- /* Construct the virtual sites for the initial configuration */
- construct_vsites(fplog,vsite,state->x,nrnb,ir->delta_t,NULL,
- top->idef.iparams,top->idef.il,
- fr->ePBC,fr->bMolPBC,graph,cr,state->box);
- }
- }
-
- debug_gmx();
-
- /* I'm assuming we need global communication the first time! MRS */
- cglo_flags = (CGLO_TEMPERATURE | CGLO_GSTAT
- | (bVV ? CGLO_PRESSURE:0)
- | (bVV ? CGLO_CONSTRAINT:0)
- | (bRerunMD ? CGLO_RERUNMD:0)
- | ((Flags & MD_READ_EKIN) ? CGLO_READEKIN:0));
-
- bSumEkinhOld = FALSE;
- compute_globals(fplog,gstat,cr,ir,fr,ekind,state,state_global,mdatoms,nrnb,vcm,
- wcycle,enerd,force_vir,shake_vir,total_vir,pres,mu_tot,
- constr,NULL,FALSE,state->box,
- top_global,&pcurr,top_global->natoms,&bSumEkinhOld,cglo_flags);
- if (ir->eI == eiVVAK) {
- /* a second call to get the half step temperature initialized as well */
- /* we do the same call as above, but turn the pressure off -- internally, this
- is recognized as a velocity verlet half-step kinetic energy calculation.
- This minimized excess variables, but perhaps loses some logic?*/
-
- compute_globals(fplog,gstat,cr,ir,fr,ekind,state,state_global,mdatoms,nrnb,vcm,
- wcycle,enerd,force_vir,shake_vir,total_vir,pres,mu_tot,
- constr,NULL,FALSE,state->box,
- top_global,&pcurr,top_global->natoms,&bSumEkinhOld,
- cglo_flags &~ CGLO_PRESSURE);
- }
-
- /* Calculate the initial half step temperature, and save the ekinh_old */
- if (!(Flags & MD_STARTFROMCPT))
- {
- for(i=0; (i<ir->opts.ngtc); i++)
- {
- copy_mat(ekind->tcstat[i].ekinh,ekind->tcstat[i].ekinh_old);
- }
- }
- if (ir->eI != eiVV)
- {
- enerd->term[F_TEMP] *= 2; /* result of averages being done over previous and current step,
- and there is no previous step */
- }
- temp0 = enerd->term[F_TEMP];
-
- /* if using an iterative algorithm, we need to create a working directory for the state. */
- if (bIterations)
- {
- bufstate = init_bufstate(state);
- }
- if (bFFscan)
- {
- snew(xcopy,state->natoms);
- snew(vcopy,state->natoms);
- copy_rvecn(state->x,xcopy,0,state->natoms);
- copy_rvecn(state->v,vcopy,0,state->natoms);
- copy_mat(state->box,boxcopy);
- }
-
- /* need to make an initiation call to get the Trotter variables set, as well as other constants for non-trotter
- temperature control */
- trotter_seq = init_npt_vars(ir,state,&MassQ,bTrotter);
-
- if (MASTER(cr))
- {
- if (constr && !ir->bContinuation && ir->eConstrAlg == econtLINCS)
- {
- fprintf(fplog,
- "RMS relative constraint deviation after constraining: %.2e\n",
- constr_rmsd(constr,FALSE));
- }
- fprintf(fplog,"Initial temperature: %g K\n",enerd->term[F_TEMP]);
- if (bRerunMD)
- {
- fprintf(stderr,"starting md rerun '%s', reading coordinates from"
- " input trajectory '%s'\n\n",
- *(top_global->name),opt2fn("-rerun",nfile,fnm));
- if (bVerbose)
- {
- fprintf(stderr,"Calculated time to finish depends on nsteps from "
- "run input file,\nwhich may not correspond to the time "
- "needed to process input trajectory.\n\n");
- }
- }
- else
- {
- char tbuf[20];
- fprintf(stderr,"starting mdrun '%s'\n",
- *(top_global->name));
- if (ir->nsteps >= 0)
- {
- sprintf(tbuf,"%8.1f",(ir->init_step+ir->nsteps)*ir->delta_t);
- }
- else
- {
- sprintf(tbuf,"%s","infinite");
- }
- if (ir->init_step > 0)
- {
- fprintf(stderr,"%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
- gmx_step_str(ir->init_step+ir->nsteps,sbuf),tbuf,
- gmx_step_str(ir->init_step,sbuf2),
- ir->init_step*ir->delta_t);
- }
- else
- {
- fprintf(stderr,"%s steps, %s ps.\n",
- gmx_step_str(ir->nsteps,sbuf),tbuf);
- }
- }
- fprintf(fplog,"\n");
- }
-
- /* Set and write start time */
- runtime_start(runtime);
- print_date_and_time(fplog,cr->nodeid,"Started mdrun",runtime);
- wallcycle_start(wcycle,ewcRUN);
- if (fplog)
- fprintf(fplog,"\n");
-
- /* safest point to do file checkpointing is here. More general point would be immediately before integrator call */
-/*#ifdef GMX_FAHCORE
- chkpt_ret=fcCheckPointParallel( cr->nodeid,
- NULL,0);
- if ( chkpt_ret == 0 )
- gmx_fatal( 3,__FILE__,__LINE__, "Checkpoint error on step %d\n", 0 );
-#endif*/
-
- debug_gmx();
- /***********************************************************
- *
- * Loop over MD steps
- *
- ************************************************************/
-
- /* if rerunMD then read coordinates and velocities from input trajectory */
- if (bRerunMD)
- {
- if (getenv("GMX_FORCE_UPDATE"))
- {
- bForceUpdate = TRUE;
- }
-
- bNotLastFrame = read_first_frame(oenv,&status,
- opt2fn("-rerun",nfile,fnm),
- &rerun_fr,TRX_NEED_X | TRX_READ_V);
- if (rerun_fr.natoms != top_global->natoms)
- {
- gmx_fatal(FARGS,
- "Number of atoms in trajectory (%d) does not match the "
- "run input file (%d)\n",
- rerun_fr.natoms,top_global->natoms);
- }
- if (ir->ePBC != epbcNONE)
- {
- if (!rerun_fr.bBox)
- {
- gmx_fatal(FARGS,"Rerun trajectory frame step %d time %f does not contain a box, while pbc is used",rerun_fr.step,rerun_fr.time);
- }
- if (max_cutoff2(ir->ePBC,rerun_fr.box) < sqr(fr->rlistlong))
- {
- gmx_fatal(FARGS,"Rerun trajectory frame step %d time %f has too small box dimensions",rerun_fr.step,rerun_fr.time);
- }
-
- /* Set the shift vectors.
- * Necessary here when have a static box different from the tpr box.
- */
- calc_shifts(rerun_fr.box,fr->shift_vec);
- }
- }
-
- /* loop over MD steps or if rerunMD to end of input trajectory */
- bFirstStep = TRUE;
- /* Skip the first Nose-Hoover integration when we get the state from tpx */
- bStateFromTPX = !opt2bSet("-cpi",nfile,fnm);
- bInitStep = bFirstStep && (bStateFromTPX || bVV);
- bStartingFromCpt = (Flags & MD_STARTFROMCPT) && bInitStep;
- bLastStep = FALSE;
- bSumEkinhOld = FALSE;
- bExchanged = FALSE;
-
- init_global_signals(&gs,cr,ir,repl_ex_nst);
-
- step = ir->init_step;
- step_rel = 0;
-
- if (ir->nstlist == -1)
- {
- init_nlistheuristics(&nlh,bGStatEveryStep,step);
- }
-
- bLastStep = (bRerunMD || (ir->nsteps >= 0 && step_rel > ir->nsteps));
- while (!bLastStep || (bRerunMD && bNotLastFrame)) {
-
- wallcycle_start(wcycle,ewcSTEP);
-
- GMX_MPE_LOG(ev_timestep1);
-
- if (bRerunMD) {
- if (rerun_fr.bStep) {
- step = rerun_fr.step;
- step_rel = step - ir->init_step;
- }
- if (rerun_fr.bTime) {
- t = rerun_fr.time;
- }
- else
- {
- t = step;
- }
- }
- else
- {
- bLastStep = (step_rel == ir->nsteps);
- t = t0 + step*ir->delta_t;
- }
-
- if (ir->efep != efepNO)
- {
- if (bRerunMD && rerun_fr.bLambda && (ir->delta_lambda!=0))
- {
- state_global->lambda = rerun_fr.lambda;
- }
- else
- {
- state_global->lambda = lam0 + step*ir->delta_lambda;
- }
- state->lambda = state_global->lambda;
- bDoDHDL = do_per_step(step,ir->nstdhdl);
- }
-
- if (bSimAnn)
- {
- update_annealing_target_temp(&(ir->opts),t);
- }
-
- if (bRerunMD)
- {
- if (!(DOMAINDECOMP(cr) && !MASTER(cr)))
- {
- for(i=0; i<state_global->natoms; i++)
- {
- copy_rvec(rerun_fr.x[i],state_global->x[i]);
- }
- if (rerun_fr.bV)
- {
- for(i=0; i<state_global->natoms; i++)
- {
- copy_rvec(rerun_fr.v[i],state_global->v[i]);
- }
- }
- else
- {
- for(i=0; i<state_global->natoms; i++)
- {
- clear_rvec(state_global->v[i]);
- }
- if (bRerunWarnNoV)
- {
- fprintf(stderr,"\nWARNING: Some frames do not contain velocities.\n"
- " Ekin, temperature and pressure are incorrect,\n"
- " the virial will be incorrect when constraints are present.\n"
- "\n");
- bRerunWarnNoV = FALSE;
- }
- }
- }
- copy_mat(rerun_fr.box,state_global->box);
- copy_mat(state_global->box,state->box);
-
- if (vsite && (Flags & MD_RERUN_VSITE))
- {
- if (DOMAINDECOMP(cr))
- {
- gmx_fatal(FARGS,"Vsite recalculation with -rerun is not implemented for domain decomposition, use particle decomposition");
- }
- if (graph)
- {
- /* Following is necessary because the graph may get out of sync
- * with the coordinates if we only have every N'th coordinate set
- */
- mk_mshift(fplog,graph,fr->ePBC,state->box,state->x);
- shift_self(graph,state->box,state->x);
- }
- construct_vsites(fplog,vsite,state->x,nrnb,ir->delta_t,state->v,
- top->idef.iparams,top->idef.il,
- fr->ePBC,fr->bMolPBC,graph,cr,state->box);
- if (graph)
- {
- unshift_self(graph,state->box,state->x);
- }
- }
- }
-
- /* Stop Center of Mass motion */
- bStopCM = (ir->comm_mode != ecmNO && do_per_step(step,ir->nstcomm));
-
- /* Copy back starting coordinates in case we're doing a forcefield scan */
- if (bFFscan)
- {
- for(ii=0; (ii<state->natoms); ii++)
- {
- copy_rvec(xcopy[ii],state->x[ii]);
- copy_rvec(vcopy[ii],state->v[ii]);
- }
- copy_mat(boxcopy,state->box);
- }
-
- if (bRerunMD)
- {
- /* for rerun MD always do Neighbour Searching */
- bNS = (bFirstStep || ir->nstlist != 0);
- bNStList = bNS;
- }
- else
- {
- /* Determine whether or not to do Neighbour Searching and LR */
- bNStList = (ir->nstlist > 0 && step % ir->nstlist == 0);
-
- bNS = (bFirstStep || bExchanged || bNStList ||
- (ir->nstlist == -1 && nlh.nabnsb > 0));
-
- if (bNS && ir->nstlist == -1)
- {
- set_nlistheuristics(&nlh,bFirstStep || bExchanged,step);
- }
- }
-
- /* < 0 means stop at next step, > 0 means stop at next NS step */
- if ( (gs.set[eglsSTOPCOND] < 0 ) ||
- ( (gs.set[eglsSTOPCOND] > 0 ) && ( bNS || ir->nstlist==0)) )
- {
- bLastStep = TRUE;
- }
-
- /* Determine whether or not to update the Born radii if doing GB */
- bBornRadii=bFirstStep;
- if (ir->implicit_solvent && (step % ir->nstgbradii==0))
- {
- bBornRadii=TRUE;
- }
-
- do_log = do_per_step(step,ir->nstlog) || bFirstStep || bLastStep;
- do_verbose = bVerbose &&
- (step % stepout == 0 || bFirstStep || bLastStep);
-
- if (bNS && !(bFirstStep && ir->bContinuation && !bRerunMD))
- {
- if (bRerunMD)
- {
- bMasterState = TRUE;
- }
- else
- {
- bMasterState = FALSE;
- /* Correct the new box if it is too skewed */
- if (DYNAMIC_BOX(*ir))
- {
- if (correct_box(fplog,step,state->box,graph))
- {
- bMasterState = TRUE;
- }
- }
- if (DOMAINDECOMP(cr) && bMasterState)
- {
- dd_collect_state(cr->dd,state,state_global);
- }
- }
-
- if (DOMAINDECOMP(cr))
- {
- /* Repartition the domain decomposition */
- wallcycle_start(wcycle,ewcDOMDEC);
- dd_partition_system(fplog,step,cr,
- bMasterState,nstglobalcomm,
- state_global,top_global,ir,
- state,&f,mdatoms,top,fr,
- vsite,shellfc,constr,
- nrnb,wcycle,do_verbose);
- wallcycle_stop(wcycle,ewcDOMDEC);
- /* If using an iterative integrator, reallocate space to match the decomposition */
- }
- }
-
- if (MASTER(cr) && do_log && !bFFscan)
- {
- print_ebin_header(fplog,step,t,state->lambda);
- }
-
- if (ir->efep != efepNO)
- {
- update_mdatoms(mdatoms,state->lambda);
- }
-
- if (bRerunMD && rerun_fr.bV)
- {
-
- /* We need the kinetic energy at minus the half step for determining
- * the full step kinetic energy and possibly for T-coupling.*/
- /* This may not be quite working correctly yet . . . . */
- compute_globals(fplog,gstat,cr,ir,fr,ekind,state,state_global,mdatoms,nrnb,vcm,
- wcycle,enerd,NULL,NULL,NULL,NULL,mu_tot,
- constr,NULL,FALSE,state->box,
- top_global,&pcurr,top_global->natoms,&bSumEkinhOld,
- CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
- }
- clear_mat(force_vir);
-
- /* Ionize the atoms if necessary */
-/* if (bIonize)
- {
- ionize(fplog,oenv,mdatoms,top_global,t,ir,state->x,state->v,
- mdatoms->start,mdatoms->start+mdatoms->homenr,state->box,cr);
- }*/
-
- /* Update force field in ffscan program */
-/* if (bFFscan)
- {
- if (update_forcefield(fplog,
- nfile,fnm,fr,
- mdatoms->nr,state->x,state->box)) {
- if (gmx_parallel_env_initialized())
- {
- gmx_finalize();
- }
- exit(0);
- }
- }*/
-
- GMX_MPE_LOG(ev_timestep2);
-
- /* We write a checkpoint at this MD step when:
- * either at an NS step when we signalled through gs,
- * or at the last step (but not when we do not want confout),
- * but never at the first step or with rerun.
- */
-/* bCPT = (((gs.set[eglsCHKPT] && bNS) ||
- (bLastStep && (Flags & MD_CONFOUT))) &&
- step > ir->init_step && !bRerunMD);
- if (bCPT)
- {
- gs.set[eglsCHKPT] = 0;
- }*/
-
- /* Determine the energy and pressure:
- * at nstcalcenergy steps and at energy output steps (set below).
- */
- bNstEner = (bGStatEveryStep || do_per_step(step,ir->nstcalcenergy));
- bCalcEnerPres = bNstEner;
-
- /* Do we need global communication ? */
- bGStat = (bCalcEnerPres || bStopCM ||
- (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck));
-
- do_ene = (do_per_step(step,ir->nstenergy) || bLastStep);
-
- if (do_ene || do_log)
- {
- bCalcEnerPres = TRUE;
- bGStat = TRUE;
- }
-
- /* these CGLO_ options remain the same throughout the iteration */
- cglo_flags = ((bRerunMD ? CGLO_RERUNMD : 0) |
- (bStopCM ? CGLO_STOPCM : 0) |
- (bGStat ? CGLO_GSTAT : 0)
- );
-
- force_flags = (GMX_FORCE_STATECHANGED |
- ((DYNAMIC_BOX(*ir) || bRerunMD) ? GMX_FORCE_DYNAMICBOX : 0) |
- GMX_FORCE_ALLFORCES |
- (bNStList ? GMX_FORCE_DOLR : 0) |
- GMX_FORCE_SEPLRF |
- (bCalcEnerPres ? GMX_FORCE_VIRIAL : 0) |
- (bDoDHDL ? GMX_FORCE_DHDL : 0)
- );
-
- if (shellfc)
- {
- /* Now is the time to relax the shells */
- count=relax_shell_flexcon(fplog,cr,bVerbose,bFFscan ? step+1 : step,
- ir,bNS,force_flags,
- bStopCM,top,top_global,
- constr,enerd,fcd,
- state,f,force_vir,mdatoms,
- nrnb,wcycle,graph,groups,
- shellfc,fr,bBornRadii,t,mu_tot,
- state->natoms,&bConverged,vsite,
- outf->fp_field);
- tcount+=count;
-
- if (bConverged)
- {
- nconverged++;
- }
- }
- else
- {
- /* The coordinates (x) are shifted (to get whole molecules)
- * in do_force.
- * This is parallellized as well, and does communication too.
- * Check comments in sim_util.c
- */
-
- do_force(fplog,cr,ir,step,nrnb,wcycle,top,top_global,groups,
- state->box,state->x,&state->hist,
- f,force_vir,mdatoms,enerd,fcd,
- state->lambda,graph,
- fr,vsite,mu_tot,t,outf->fp_field,ed,bBornRadii,
- (bNS ? GMX_FORCE_NS : 0) | force_flags);
- }
-
- GMX_BARRIER(cr->mpi_comm_mygroup);
-
- /* if (bTCR)
- {
- mu_aver = calc_mu_aver(cr,state->x,mdatoms->chargeA,
- mu_tot,&top_global->mols,mdatoms,gnx,grpindex);
- }
-
- if (bTCR && bFirstStep)
- {
- tcr=init_coupling(fplog,nfile,fnm,cr,fr,mdatoms,&(top->idef));
- fprintf(fplog,"Done init_coupling\n");
- fflush(fplog);
- }*/
-
- /* ############### START FIRST UPDATE HALF-STEP ############### */
-
- if (bVV && !bStartingFromCpt && !bRerunMD)
- {
- if (ir->eI == eiVV)
- {
- if (bInitStep)
- {
- /* if using velocity verlet with full time step Ekin,
- * take the first half step only to compute the
- * virial for the first step. From there,
- * revert back to the initial coordinates
- * so that the input is actually the initial step.
- */
- copy_rvecn(state->v,cbuf,0,state->natoms); /* should make this better for parallelizing? */
- }
-
- /* this is for NHC in the Ekin(t+dt/2) version of vv */
- if (!bInitStep)
- {
- trotter_update(ir,step,ekind,enerd,state,total_vir,mdatoms,&MassQ,trotter_seq,ettTSEQ2);
- }
-
- if (ir->eI == eiVVAK)
- {
- update_tcouple(fplog,step,ir,state,ekind,wcycle,upd,&MassQ,mdatoms);
- }
-
- update_coords(fplog,step,ir,mdatoms,state,
- f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
- ekind,M,wcycle,upd,bInitStep,etrtVELOCITY1,
- cr,nrnb,constr,&top->idef);
-
- if (bIterations)
- {
- gmx_iterate_init(&iterate,bIterations && !bInitStep);
- }
- /* for iterations, we save these vectors, as we will be self-consistently iterating
- the calculations */
- /*#### UPDATE EXTENDED VARIABLES IN TROTTER FORMULATION */
-
- /* save the state */
- if (bIterations && iterate.bIterate) {
- copy_coupling_state(state,bufstate,ekind,ekind_save,&(ir->opts));
- }
- }
-
- bFirstIterate = TRUE;
- while (bFirstIterate || (bIterations && iterate.bIterate))
- {
- if (bIterations && iterate.bIterate)
- {
- copy_coupling_state(bufstate,state,ekind_save,ekind,&(ir->opts));
- if (bFirstIterate && bTrotter)
- {
- /* The first time through, we need a decent first estimate
- of veta(t+dt) to compute the constraints. Do
- this by computing the box volume part of the
- trotter integration at this time. Nothing else
- should be changed by this routine here. If
- !(first time), we start with the previous value
- of veta. */
-
- veta_save = state->veta;
- trotter_update(ir,step,ekind,enerd,state,total_vir,mdatoms,&MassQ,trotter_seq,ettTSEQ0);
- vetanew = state->veta;
- state->veta = veta_save;
- }
- }
-
- bOK = TRUE;
- if ( !bRerunMD || rerun_fr.bV || bForceUpdate) { /* Why is rerun_fr.bV here? Unclear. */
- dvdl = 0;
-
- update_constraints(fplog,step,&dvdl,ir,ekind,mdatoms,state,graph,f,
- &top->idef,shake_vir,NULL,
- cr,nrnb,wcycle,upd,constr,
- bInitStep,TRUE,bCalcEnerPres,vetanew);
-
- if (!bOK && !bFFscan)
- {
- gmx_fatal(FARGS,"Constraint error: Shake, Lincs or Settle could not solve the constrains");
- }
-
- }
- else if (graph)
- { /* Need to unshift here if a do_force has been
- called in the previous step */
- unshift_self(graph,state->box,state->x);
- }
-
-
- if (bVV) {
- /* if VV, compute the pressure and constraints */
- /* if VV2, the pressure and constraints only if using pressure control.*/
- bPres = (ir->eI==eiVV || IR_NPT_TROTTER(ir));
- bTemp = ((ir->eI==eiVV &&(!bInitStep)) || (ir->eI==eiVVAK && IR_NPT_TROTTER(ir)));
- compute_globals(fplog,gstat,cr,ir,fr,ekind,state,state_global,mdatoms,nrnb,vcm,
- wcycle,enerd,force_vir,shake_vir,total_vir,pres,mu_tot,
- constr,NULL,FALSE,state->box,
- top_global,&pcurr,top_global->natoms,&bSumEkinhOld,
- cglo_flags
- | CGLO_ENERGY
- | (bTemp ? CGLO_TEMPERATURE:0)
- | (bPres ? CGLO_PRESSURE : 0)
- | (bPres ? CGLO_CONSTRAINT : 0)
- | (iterate.bIterate ? CGLO_ITERATE : 0)
- | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
- | CGLO_SCALEEKIN
- );
- }
- /* explanation of above:
- a) We compute Ekin at the full time step
- if 1) we are using the AveVel Ekin, and it's not the
- initial step, or 2) if we are using AveEkin, but need the full
- time step kinetic energy for the pressure.
- b) If we are using EkinAveEkin for the kinetic energy for the temperture control, we still feed in
- EkinAveVel because it's needed for the pressure */
-
- /* temperature scaling and pressure scaling to produce the extended variables at t+dt */
- if (bVV && !bInitStep)
- {
- trotter_update(ir,step,ekind,enerd,state,total_vir,mdatoms,&MassQ, trotter_seq,ettTSEQ2);
- }
-
- if (bIterations &&
- done_iterating(cr,fplog,step,&iterate,bFirstIterate,
- state->veta,&vetanew))
- {
- break;
- }
- bFirstIterate = FALSE;
- }
-
- if (bTrotter && !bInitStep) {
- copy_mat(shake_vir,state->svir_prev);
- copy_mat(force_vir,state->fvir_prev);
- if (IR_NVT_TROTTER(ir) && ir->eI==eiVV) {
- /* update temperature and kinetic energy now that step is over - this is the v(t+dt) point */
- enerd->term[F_TEMP] = sum_ekin(&(ir->opts),ekind,NULL,(ir->eI==eiVV),FALSE,FALSE);
- enerd->term[F_EKIN] = trace(ekind->ekin);
- }
- }
- /* if it's the initial step, we performed this first step just to get the constraint virial */
- if (bInitStep && ir->eI==eiVV) {
- copy_rvecn(cbuf,state->v,0,state->natoms);
- }
-
- if (fr->bSepDVDL && fplog && do_log)
- {
- fprintf(fplog,sepdvdlformat,"Constraint",0.0,dvdl);
- }
- enerd->term[F_DHDL_CON] += dvdl;
-
- GMX_MPE_LOG(ev_timestep1);
-
- }
-
- /* MRS -- now done iterating -- compute the conserved quantity */
- if (bVV) {
- last_conserved = 0;
- if (IR_NVT_TROTTER(ir) || IR_NPT_TROTTER(ir))
- {
- last_conserved =
- NPT_energy(ir,state,&MassQ);
- if ((ir->eDispCorr != edispcEnerPres) && (ir->eDispCorr != edispcAllEnerPres))
- {
- last_conserved -= enerd->term[F_DISPCORR];
- }
- }
- if (ir->eI==eiVV) {
- last_ekin = enerd->term[F_EKIN]; /* does this get preserved through checkpointing? */
- }
- }
-
- /* ######## END FIRST UPDATE STEP ############## */
- /* ######## If doing VV, we now have v(dt) ###### */
-
- /* ################## START TRAJECTORY OUTPUT ################# */
-
- /* Now we have the energies and forces corresponding to the
- * coordinates at time t. We must output all of this before
- * the update.
- * for RerunMD t is read from input trajectory
- */
- GMX_MPE_LOG(ev_output_start);
-
- mdof_flags = 0;
- if (do_per_step(step,ir->nstxout)) { mdof_flags |= MDOF_X; }
- if (do_per_step(step,ir->nstvout)) { mdof_flags |= MDOF_V; }
- if (do_per_step(step,ir->nstfout)) { mdof_flags |= MDOF_F; }
- if (do_per_step(step,ir->nstxtcout)) { mdof_flags |= MDOF_XTC; }
-/* if (bCPT) { mdof_flags |= MDOF_CPT; };*/
-
-#ifdef GMX_FAHCORE
- if (MASTER(cr))
- fcReportProgress( ir->nsteps, step );
-
- if (bLastStep)
- {
- /* Enforce writing positions and velocities at end of run */
- mdof_flags |= (MDOF_X | MDOF_V);
- }
- /* sync bCPT and fc record-keeping */
-/* if (bCPT && MASTER(cr))
- fcRequestCheckPoint();*/
-#endif
-
- if (mdof_flags != 0)
- {
- wallcycle_start(wcycle,ewcTRAJ);
-/* if (bCPT)
- {
- if (state->flags & (1<<estLD_RNG))
- {
- get_stochd_state(upd,state);
- }
- if (MASTER(cr))
- {
- if (bSumEkinhOld)
- {
- state_global->ekinstate.bUpToDate = FALSE;
- }
- else
- {
- update_ekinstate(&state_global->ekinstate,ekind);
- state_global->ekinstate.bUpToDate = TRUE;
- }
- update_energyhistory(&state_global->enerhist,mdebin);
- }
- }*/
- write_traj(fplog,cr,outf,mdof_flags,top_global,
- step,t,state,state_global,f,f_global,&n_xtc,&x_xtc);
-/* if (bCPT)
- {
- nchkpt++;
- bCPT = FALSE;
- }*/
- debug_gmx();
- if (bLastStep && step_rel == ir->nsteps &&
- (Flags & MD_CONFOUT) && MASTER(cr) &&
- !bRerunMD && !bFFscan)
- {
- /* x and v have been collected in write_traj,
- * because a checkpoint file will always be written
- * at the last step.
- */
- fprintf(stderr,"\nWriting final coordinates.\n");
- if (ir->ePBC != epbcNONE && !ir->bPeriodicMols &&
- DOMAINDECOMP(cr))
- {
- /* Make molecules whole only for confout writing */
- do_pbc_mtop(fplog,ir->ePBC,state->box,top_global,state_global->x);
- }
-/* write_sto_conf_mtop(ftp2fn(efSTO,nfile,fnm),
- *top_global->name,top_global,
- state_global->x,state_global->v,
- ir->ePBC,state->box);*/
- debug_gmx();
- }
- wallcycle_stop(wcycle,ewcTRAJ);
- }
- GMX_MPE_LOG(ev_output_finish);
-
- /* kludge -- virial is lost with restart for NPT control. Must restart */
- if (bStartingFromCpt && bVV)
- {
- copy_mat(state->svir_prev,shake_vir);
- copy_mat(state->fvir_prev,force_vir);
- }
- /* ################## END TRAJECTORY OUTPUT ################ */
-
- /* Determine the pressure:
- * always when we want exact averages in the energy file,
- * at ns steps when we have pressure coupling,
- * otherwise only at energy output steps (set below).
- */
-
- bNstEner = (bGStatEveryStep || do_per_step(step,ir->nstcalcenergy));
- bCalcEnerPres = bNstEner;
-
- /* Do we need global communication ? */
- bGStat = (bGStatEveryStep || bStopCM || bNS ||
- (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck));
-
- do_ene = (do_per_step(step,ir->nstenergy) || bLastStep);
-
- if (do_ene || do_log)
- {
- bCalcEnerPres = TRUE;
- bGStat = TRUE;
- }
-
- /* Determine the wallclock run time up till now */
- run_time = gmx_gettime() - (double)runtime->real;
-
- /* Check whether everything is still allright */
- if (((int)gmx_get_stop_condition() > handled_stop_condition)
-#ifdef GMX_THREADS
- && MASTER(cr)
-#endif
- )
- {
- /* this is just make gs.sig compatible with the hack
- of sending signals around by MPI_Reduce with together with
- other floats */
- if ( gmx_get_stop_condition() == gmx_stop_cond_next_ns )
- gs.sig[eglsSTOPCOND]=1;
- if ( gmx_get_stop_condition() == gmx_stop_cond_next )
- gs.sig[eglsSTOPCOND]=-1;
- /* < 0 means stop at next step, > 0 means stop at next NS step */
- if (fplog)
- {
- fprintf(fplog,
- "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
- gmx_get_signal_name(),
- gs.sig[eglsSTOPCOND]==1 ? "NS " : "");
- fflush(fplog);
- }
- fprintf(stderr,
- "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
- gmx_get_signal_name(),
- gs.sig[eglsSTOPCOND]==1 ? "NS " : "");
- fflush(stderr);
- handled_stop_condition=(int)gmx_get_stop_condition();
- }
- else if (MASTER(cr) && (bNS || ir->nstlist <= 0) &&
- (max_hours > 0 && run_time > max_hours*60.0*60.0*0.99) &&
- gs.sig[eglsSTOPCOND] == 0 && gs.set[eglsSTOPCOND] == 0)
- {
- /* Signal to terminate the run */
- gs.sig[eglsSTOPCOND] = 1;
- if (fplog)
- {
- fprintf(fplog,"\nStep %s: Run time exceeded %.3f hours, will terminate the run\n",gmx_step_str(step,sbuf),max_hours*0.99);
- }
- fprintf(stderr, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n",gmx_step_str(step,sbuf),max_hours*0.99);
- }
-
- if (bResetCountersHalfMaxH && MASTER(cr) &&
- run_time > max_hours*60.0*60.0*0.495)
- {
- gs.sig[eglsRESETCOUNTERS] = 1;
- }
-
- if (ir->nstlist == -1 && !bRerunMD)
- {
- /* When bGStatEveryStep=FALSE, global_stat is only called
- * when we check the atom displacements, not at NS steps.
- * This means that also the bonded interaction count check is not
- * performed immediately after NS. Therefore a few MD steps could
- * be performed with missing interactions.
- * But wrong energies are never written to file,
- * since energies are only written after global_stat
- * has been called.
- */
- if (step >= nlh.step_nscheck)
- {
- nlh.nabnsb = natoms_beyond_ns_buffer(ir,fr,&top->cgs,
- nlh.scale_tot,state->x);
- }
- else
- {
- /* This is not necessarily true,
- * but step_nscheck is determined quite conservatively.
- */
- nlh.nabnsb = 0;
- }
- }
-
- /* In parallel we only have to check for checkpointing in steps
- * where we do global communication,
- * otherwise the other nodes don't know.
- */
- if (MASTER(cr) && ((bGStat || !PAR(cr)) &&
- cpt_period >= 0 &&
- (cpt_period == 0 ||
- run_time >= nchkpt*cpt_period*60.0)) &&
- gs.set[eglsCHKPT] == 0)
- {
- gs.sig[eglsCHKPT] = 1;
- }
-
- if (bIterations)
- {
- gmx_iterate_init(&iterate,bIterations);
- }
-
- /* for iterations, we save these vectors, as we will be redoing the calculations */
- if (bIterations && iterate.bIterate)
- {
- copy_coupling_state(state,bufstate,ekind,ekind_save,&(ir->opts));
- }
- bFirstIterate = TRUE;
- while (bFirstIterate || (bIterations && iterate.bIterate))
- {
- /* We now restore these vectors to redo the calculation with improved extended variables */
- if (bIterations)
- {
- copy_coupling_state(bufstate,state,ekind_save,ekind,&(ir->opts));
- }
-
- /* We make the decision to break or not -after- the calculation of Ekin and Pressure,
- so scroll down for that logic */
-
- /* ######### START SECOND UPDATE STEP ################# */
- GMX_MPE_LOG(ev_update_start);
- bOK = TRUE;
- if (!bRerunMD || rerun_fr.bV || bForceUpdate)
- {
- wallcycle_start(wcycle,ewcUPDATE);
- dvdl = 0;
- /* Box is changed in update() when we do pressure coupling,
- * but we should still use the old box for energy corrections and when
- * writing it to the energy file, so it matches the trajectory files for
- * the same timestep above. Make a copy in a separate array.
- */
- copy_mat(state->box,lastbox);
- /* UPDATE PRESSURE VARIABLES IN TROTTER FORMULATION WITH CONSTRAINTS */
- if (bTrotter)
- {
- if (bIterations && iterate.bIterate)
- {
- if (bFirstIterate)
- {
- scalevir = 1;
- }
- else
- {
- /* we use a new value of scalevir to converge the iterations faster */
- scalevir = tracevir/trace(shake_vir);
- }
- msmul(shake_vir,scalevir,shake_vir);
- m_add(force_vir,shake_vir,total_vir);
- clear_mat(shake_vir);
- }
- trotter_update(ir,step,ekind,enerd,state,total_vir,mdatoms,&MassQ, trotter_seq,ettTSEQ3);
- }
- /* We can only do Berendsen coupling after we have summed
- * the kinetic energy or virial. Since the happens
- * in global_state after update, we should only do it at
- * step % nstlist = 1 with bGStatEveryStep=FALSE.
- */
-
- if (ir->eI != eiVVAK)
- {
- update_tcouple(fplog,step,ir,state,ekind,wcycle,upd,&MassQ,mdatoms);
- }
- update_pcouple(fplog,step,ir,state,pcoupl_mu,M,wcycle,
- upd,bInitStep);
-
- if (bVV)
- {
- /* velocity half-step update */
- update_coords(fplog,step,ir,mdatoms,state,f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
- ekind,M,wcycle,upd,FALSE,etrtVELOCITY2,cr,nrnb,constr,&top->idef);
- }
-
- /* Above, initialize just copies ekinh into ekin,
- * it doesn't copy position (for VV),
- * and entire integrator for MD.
- */
-
- if (ir->eI==eiVVAK)
- {
- copy_rvecn(state->x,cbuf,0,state->natoms);
- }
-
- update_coords(fplog,step,ir,mdatoms,state,f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
- ekind,M,wcycle,upd,bInitStep,etrtPOSITION,cr,nrnb,constr,&top->idef);
- wallcycle_stop(wcycle,ewcUPDATE);
-
- update_constraints(fplog,step,&dvdl,ir,ekind,mdatoms,state,graph,f,
- &top->idef,shake_vir,force_vir,
- cr,nrnb,wcycle,upd,constr,
- bInitStep,FALSE,bCalcEnerPres,state->veta);
-
- if (ir->eI==eiVVAK)
- {
- /* erase F_EKIN and F_TEMP here? */
- /* just compute the kinetic energy at the half step to perform a trotter step */
- compute_globals(fplog,gstat,cr,ir,fr,ekind,state,state_global,mdatoms,nrnb,vcm,
- wcycle,enerd,force_vir,shake_vir,total_vir,pres,mu_tot,
- constr,NULL,FALSE,lastbox,
- top_global,&pcurr,top_global->natoms,&bSumEkinhOld,
- cglo_flags | CGLO_TEMPERATURE | CGLO_CONSTRAINT
- );
- wallcycle_start(wcycle,ewcUPDATE);
- trotter_update(ir,step,ekind,enerd,state,total_vir,mdatoms,&MassQ, trotter_seq,ettTSEQ4);
- /* now we know the scaling, we can compute the positions again again */
- copy_rvecn(cbuf,state->x,0,state->natoms);
-
- update_coords(fplog,step,ir,mdatoms,state,f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
- ekind,M,wcycle,upd,bInitStep,etrtPOSITION,cr,nrnb,constr,&top->idef);
- wallcycle_stop(wcycle,ewcUPDATE);
-
- /* do we need an extra constraint here? just need to copy out of state->v to upd->xp? */
- /* are the small terms in the shake_vir here due
- * to numerical errors, or are they important
- * physically? I'm thinking they are just errors, but not completely sure.
- * For now, will call without actually constraining, constr=NULL*/
- update_constraints(fplog,step,&dvdl,ir,ekind,mdatoms,state,graph,f,
- &top->idef,tmp_vir,force_vir,
- cr,nrnb,wcycle,upd,NULL,
- bInitStep,FALSE,bCalcEnerPres,state->veta);
- }
- if (!bOK && !bFFscan)
- {
- gmx_fatal(FARGS,"Constraint error: Shake, Lincs or Settle could not solve the constrains");
- }
-
- if (fr->bSepDVDL && fplog && do_log)
- {
- fprintf(fplog,sepdvdlformat,"Constraint",0.0,dvdl);
- }
- enerd->term[F_DHDL_CON] += dvdl;
- }
- else if (graph)
- {
- /* Need to unshift here */
- unshift_self(graph,state->box,state->x);
- }
-
- GMX_BARRIER(cr->mpi_comm_mygroup);
- GMX_MPE_LOG(ev_update_finish);
-
- if (vsite != NULL)
- {
- wallcycle_start(wcycle,ewcVSITECONSTR);
- if (graph != NULL)
- {
- shift_self(graph,state->box,state->x);
- }
- construct_vsites(fplog,vsite,state->x,nrnb,ir->delta_t,state->v,
- top->idef.iparams,top->idef.il,
- fr->ePBC,fr->bMolPBC,graph,cr,state->box);
-
- if (graph != NULL)
- {
- unshift_self(graph,state->box,state->x);
- }
- wallcycle_stop(wcycle,ewcVSITECONSTR);
- }
-
- /* ############## IF NOT VV, Calculate globals HERE, also iterate constraints ############ */
- if (ir->nstlist == -1 && bFirstIterate)
- {
- gs.sig[eglsNABNSB] = nlh.nabnsb;
- }
- compute_globals(fplog,gstat,cr,ir,fr,ekind,state,state_global,mdatoms,nrnb,vcm,
- wcycle,enerd,force_vir,shake_vir,total_vir,pres,mu_tot,
- constr,
- bFirstIterate ? &gs : NULL,(step % gs.nstms == 0),
- lastbox,
- top_global,&pcurr,top_global->natoms,&bSumEkinhOld,
- cglo_flags
- | (!EI_VV(ir->eI) ? CGLO_ENERGY : 0)
- | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0)
- | (!EI_VV(ir->eI) || bRerunMD ? CGLO_PRESSURE : 0)
- | (bIterations && iterate.bIterate ? CGLO_ITERATE : 0)
- | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
- | CGLO_CONSTRAINT
- );
- if (ir->nstlist == -1 && bFirstIterate)
- {
- nlh.nabnsb = gs.set[eglsNABNSB];
- gs.set[eglsNABNSB] = 0;
- }
- /* bIterate is set to keep it from eliminating the old ekin kinetic energy terms */
- /* ############# END CALC EKIN AND PRESSURE ################# */
-
- /* Note: this is OK, but there are some numerical precision issues with using the convergence of
- the virial that should probably be addressed eventually. state->veta has better properies,
- but what we actually need entering the new cycle is the new shake_vir value. Ideally, we could
- generate the new shake_vir, but test the veta value for convergence. This will take some thought. */
-
- if (bIterations &&
- done_iterating(cr,fplog,step,&iterate,bFirstIterate,
- trace(shake_vir),&tracevir))
- {
- break;
- }
- bFirstIterate = FALSE;
- }
-
- update_box(fplog,step,ir,mdatoms,state,graph,f,
- ir->nstlist==-1 ? &nlh.scale_tot : NULL,pcoupl_mu,nrnb,wcycle,upd,bInitStep,FALSE);
-
- /* ################# END UPDATE STEP 2 ################# */
- /* #### We now have r(t+dt) and v(t+dt/2) ############# */
-
- /* The coordinates (x) were unshifted in update */
-/* if (bFFscan && (shellfc==NULL || bConverged))
- {
- if (print_forcefield(fplog,enerd->term,mdatoms->homenr,
- f,NULL,xcopy,
- &(top_global->mols),mdatoms->massT,pres))
- {
- if (gmx_parallel_env_initialized())
- {
- gmx_finalize();
- }
- fprintf(stderr,"\n");
- exit(0);
- }
- }*/
- if (!bGStat)
- {
- /* We will not sum ekinh_old,
- * so signal that we still have to do it.
- */
- bSumEkinhOld = TRUE;
- }
-
-/* if (bTCR)
- {*/
- /* Only do GCT when the relaxation of shells (minimization) has converged,
- * otherwise we might be coupling to bogus energies.
- * In parallel we must always do this, because the other sims might
- * update the FF.
- */
-
- /* Since this is called with the new coordinates state->x, I assume
- * we want the new box state->box too. / EL 20040121
- */
-/* do_coupling(fplog,oenv,nfile,fnm,tcr,t,step,enerd->term,fr,
- ir,MASTER(cr),
- mdatoms,&(top->idef),mu_aver,
- top_global->mols.nr,cr,
- state->box,total_vir,pres,
- mu_tot,state->x,f,bConverged);
- debug_gmx();
- }*/
-
- /* ######### BEGIN PREPARING EDR OUTPUT ########### */
-
- sum_dhdl(enerd,state->lambda,ir);
- /* use the directly determined last velocity, not actually the averaged half steps */
- if (bTrotter && ir->eI==eiVV)
- {
- enerd->term[F_EKIN] = last_ekin;
- }
- enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
-
- switch (ir->etc)
- {
- case etcNO:
- break;
- case etcBERENDSEN:
- break;
- case etcNOSEHOOVER:
- if (IR_NVT_TROTTER(ir)) {
- enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + last_conserved;
- } else {
- enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] +
- NPT_energy(ir,state,&MassQ);
- }
- break;
- case etcVRESCALE:
- enerd->term[F_ECONSERVED] =
- enerd->term[F_ETOT] + vrescale_energy(&(ir->opts),
- state->therm_integral);
- break;
- default:
- break;
- }
-
- /* Check for excessively large energies */
-/* if (bIonize)
- {
-#ifdef GMX_DOUBLE
- real etot_max = 1e200;
-#else
- real etot_max = 1e30;
-#endif
- if (fabs(enerd->term[F_ETOT]) > etot_max)
- {
- fprintf(stderr,"Energy too large (%g), giving up\n",
- enerd->term[F_ETOT]);
- }
- }*/
- /* ######### END PREPARING EDR OUTPUT ########### */
-
- /* Time for performance */
- if (((step % stepout) == 0) || bLastStep)
- {
- runtime_upd_proc(runtime);
- }
-
- /* Output stuff */
- if (MASTER(cr))
- {
- gmx_bool do_dr,do_or;
-
- if (!(bStartingFromCpt && (EI_VV(ir->eI))))
- {
- if (bNstEner)
- {
- upd_mdebin(mdebin,bDoDHDL,TRUE,
- t,mdatoms->tmass,enerd,state,lastbox,
- shake_vir,force_vir,total_vir,pres,
- ekind,mu_tot,constr);
- }
- else
- {
- upd_mdebin_step(mdebin);
- }
-
- do_dr = do_per_step(step,ir->nstdisreout);
- do_or = do_per_step(step,ir->nstorireout);
-
- print_ebin(outf->fp_ene,do_ene,do_dr,do_or,do_log?fplog:NULL,
- step,t,
- eprNORMAL,bCompact,mdebin,fcd,groups,&(ir->opts));
- }
- if (ir->ePull != epullNO)
- {
- pull_print_output(ir->pull,step,t);
- }
-
- if (do_per_step(step,ir->nstlog))
- {
- if(fflush(fplog) != 0)
- {
- gmx_fatal(FARGS,"Cannot flush logfile - maybe you are out of quota?");
- }
- }
- }
-
-
- /* Remaining runtime */
- if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal() ))
- {
- if (shellfc)
- {
- fprintf(stderr,"\n");
- }
- print_time(stderr,runtime,step,ir,cr);
- }
-
- /* Set new positions for the group to embed */
- if(!bLastStep){
- if(step_rel<=it_xy)
- {
- fac[0]+=xy_step;
- fac[1]+=xy_step;
- } else if (step_rel<=(it_xy+it_z))
- {
- fac[2]+=z_step;
- }
- resize(ins_at,r_ins,state_global->x,pos_ins,fac);
- }
-
- /* Replica exchange */
-/* bExchanged = FALSE;
- if ((repl_ex_nst > 0) && (step > 0) && !bLastStep &&
- do_per_step(step,repl_ex_nst))
- {
- bExchanged = replica_exchange(fplog,cr,repl_ex,
- state_global,enerd->term,
- state,step,t);
- }
- if (bExchanged && PAR(cr))
- {
- if (DOMAINDECOMP(cr))
- {
- dd_partition_system(fplog,step,cr,TRUE,1,
- state_global,top_global,ir,
- state,&f,mdatoms,top,fr,
- vsite,shellfc,constr,
- nrnb,wcycle,FALSE);
- }
- else
- {
- bcast_state(cr,state,FALSE);
- }
- }*/
-
- bFirstStep = FALSE;
- bInitStep = FALSE;
- bStartingFromCpt = FALSE;
-
- /* ####### SET VARIABLES FOR NEXT ITERATION IF THEY STILL NEED IT ###### */
- /* With all integrators, except VV, we need to retain the pressure
- * at the current step for coupling at the next step.
- */
- if ((state->flags & (1<<estPRES_PREV)) &&
- (bGStatEveryStep ||
- (ir->nstpcouple > 0 && step % ir->nstpcouple == 0)))
- {
- /* Store the pressure in t_state for pressure coupling
- * at the next MD step.
- */
- copy_mat(pres,state->pres_prev);
- }
-
- /* ####### END SET VARIABLES FOR NEXT ITERATION ###### */
-
- if (bRerunMD)
- {
- /* read next frame from input trajectory */
- bNotLastFrame = read_next_frame(oenv,status,&rerun_fr);
- }
-
- if (!bRerunMD || !rerun_fr.bStep)
- {
- /* increase the MD step number */
- step++;
- step_rel++;
- }
-
- cycles = wallcycle_stop(wcycle,ewcSTEP);
- if (DOMAINDECOMP(cr) && wcycle)
- {
- dd_cycles_add(cr->dd,cycles,ddCyclStep);
- }
-
- if (step_rel == wcycle_get_reset_counters(wcycle) ||
- gs.set[eglsRESETCOUNTERS] != 0)
- {
- /* Reset all the counters related to performance over the run */
- reset_all_counters(fplog,cr,step,&step_rel,ir,wcycle,nrnb,runtime);
- wcycle_set_reset_counters(wcycle,-1);
- bResetCountersHalfMaxH = FALSE;
- gs.set[eglsRESETCOUNTERS] = 0;
- }
- }
- /* End of main MD loop */
- debug_gmx();
- write_sto_conf_mtop(ftp2fn(efSTO,nfile,fnm),
- *top_global->name,top_global,
- state_global->x,state_global->v,
- ir->ePBC,state->box);
-
- /* Stop the time */
- runtime_end(runtime);
-
- if (bRerunMD)
- {
- close_trj(status);
- }
-
- if (!(cr->duty & DUTY_PME))
- {
- /* Tell the PME only node to finish */
- gmx_pme_finish(cr);
- }
-
- if (MASTER(cr))
- {
- if (ir->nstcalcenergy > 0 && !bRerunMD)
- {
- print_ebin(outf->fp_ene,FALSE,FALSE,FALSE,fplog,step,t,
- eprAVER,FALSE,mdebin,fcd,groups,&(ir->opts));
- }
- }
-
- done_mdoutf(outf);
-
- debug_gmx();
-
- if (ir->nstlist == -1 && nlh.nns > 0 && fplog)
- {
- fprintf(fplog,"Average neighborlist lifetime: %.1f steps, std.dev.: %.1f steps\n",nlh.s1/nlh.nns,sqrt(nlh.s2/nlh.nns - sqr(nlh.s1/nlh.nns)));
- fprintf(fplog,"Average number of atoms that crossed the half buffer length: %.1f\n\n",nlh.ab/nlh.nns);
- }
-
- if (shellfc && fplog)
- {
- fprintf(fplog,"Fraction of iterations that converged: %.2f %%\n",
- (nconverged*100.0)/step_rel);
- fprintf(fplog,"Average number of force evaluations per MD step: %.2f\n\n",
- tcount/step_rel);
- }
-
-/* if (repl_ex_nst > 0 && MASTER(cr))
- {
- print_replica_exchange_statistics(fplog,repl_ex);
- }*/
-
- runtime->nsteps_done = step_rel;
-
- return 0;
-}
-
-
-int mdrunner_membed(FILE *fplog,t_commrec *cr,int nfile,const t_filenm fnm[],
- const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
- int nstglobalcomm,
- ivec ddxyz,int dd_node_order,real rdd,real rconstr,
- const char *dddlb_opt,real dlb_scale,
- const char *ddcsx,const char *ddcsy,const char *ddcsz,
- int nstepout,int resetstep,int nmultisim,int repl_ex_nst,int repl_ex_seed,
- real pforce,real cpt_period,real max_hours,
- const char *deviceOptions,
- unsigned long Flags,
- real xy_fac, real xy_max, real z_fac, real z_max,
- int it_xy, int it_z, real probe_rad, int low_up_rm,
- int pieces, gmx_bool bALLOW_ASYMMETRY, int maxwarn)
-{
- double nodetime=0,realtime;
- t_inputrec *inputrec;
- t_state *state=NULL;
- matrix box;
- gmx_ddbox_t ddbox;
- int npme_major,npme_minor;
- real tmpr1,tmpr2;
- t_nrnb *nrnb;
- gmx_mtop_t *mtop=NULL;
- t_mdatoms *mdatoms=NULL;
- t_forcerec *fr=NULL;
- t_fcdata *fcd=NULL;
- real ewaldcoeff=0;
- gmx_pme_t *pmedata=NULL;
- gmx_vsite_t *vsite=NULL;
- gmx_constr_t constr;
- int i,m,nChargePerturbed=-1,status,nalloc;
- char *gro;
- gmx_wallcycle_t wcycle;
- gmx_bool bReadRNG,bReadEkin;
- int list;
- gmx_runtime_t runtime;
- int rc;
- gmx_large_int_t reset_counters;
- gmx_edsam_t ed=NULL;
- t_commrec *cr_old=cr;
- int nthreads=1,nthreads_requested=1;
-
-
- char *ins;
- int rm_bonded_at,fr_id,fr_i=0,tmp_id,warn=0;
- int ng,j,max_lip_rm,ins_grp_id,ins_nat,mem_nat,ntype,lip_rm,tpr_version;
- real xy_step=0,z_step=0;
- real prot_area;
- rvec *r_ins=NULL,fac;
- t_block *ins_at,*rest_at;
- pos_ins_t *pos_ins;
- mem_t *mem_p;
- rm_t *rm_p;
- gmx_groups_t *groups;
- gmx_bool bExcl=FALSE;
- t_atoms atoms;
- t_pbc *pbc;
- char **piecename=NULL;
-
- /* CAUTION: threads may be started later on in this function, so
- cr doesn't reflect the final parallel state right now */
- snew(inputrec,1);
- snew(mtop,1);
-
- if (bVerbose && SIMMASTER(cr))
- {
- fprintf(stderr,"Getting Loaded...\n");
- }
-
- if (Flags & MD_APPENDFILES)
- {
- fplog = NULL;
- }
-
- snew(state,1);
- if (MASTER(cr))
- {
- /* Read (nearly) all data required for the simulation */
- read_tpx_state(ftp2fn(efTPX,nfile,fnm),inputrec,state,NULL,mtop);
-
- /* NOW the threads will be started: */
-#ifdef GMX_THREADS
-#endif
- }
- /* END OF CAUTION: cr is now reliable */
-
- if (PAR(cr))
- {
- /* now broadcast everything to the non-master nodes/threads: */
- init_parallel(fplog, cr, inputrec, mtop);
- }
- /* now make sure the state is initialized and propagated */
- set_state_entries(state,inputrec,cr->nnodes);
-
- if (can_use_allvsall(inputrec,mtop,TRUE,cr,fplog))
- {
- /* All-vs-all loops do not work with domain decomposition */
- Flags |= MD_PARTDEC;
- }
-
- if (!EEL_PME(inputrec->coulombtype) || (Flags & MD_PARTDEC))
- {
- cr->npmenodes = 0;
- }
-
- snew(ins_at,1);
- snew(pos_ins,1);
- if(MASTER(cr))
- {
- tpr_version = get_tpr_version(ftp2fn(efTPX,nfile,fnm));
- if (tpr_version<58)
- gmx_fatal(FARGS,"Version of *.tpr file to old (%d). Rerun grompp with gromacs VERSION 4.0.3 or newer.\n",tpr_version);
-
- if( inputrec->eI != eiMD )
- gmx_input("Change integrator to md in mdp file.");
-
- if(PAR(cr))
- gmx_input("Sorry, parallel g_membed is not yet fully functrional.");
-
- groups=&(mtop->groups);
-
- atoms=gmx_mtop_global_atoms(mtop);
- snew(mem_p,1);
- fprintf(stderr,"\nSelect a group to embed in the membrane:\n");
- get_index(&atoms,ftp2fn_null(efNDX,nfile,fnm),1,&(ins_at->nr),&(ins_at->index),&ins);
- ins_grp_id = search_string(ins,groups->ngrpname,(groups->grpname));
- fprintf(stderr,"\nSelect a group to embed %s into (e.g. the membrane):\n",ins);
- get_index(&atoms,ftp2fn_null(efNDX,nfile,fnm),1,&(mem_p->mem_at.nr),&(mem_p->mem_at.index),&(mem_p->name));
-
- pos_ins->pieces=pieces;
- snew(pos_ins->nidx,pieces);
- snew(pos_ins->subindex,pieces);
- snew(piecename,pieces);
- if (pieces>1)
- {
- fprintf(stderr,"\nSelect pieces to embed:\n");
- get_index(&atoms,ftp2fn_null(efNDX,nfile,fnm),pieces,pos_ins->nidx,pos_ins->subindex,piecename);
- }
- else
- {
- /*use whole embedded group*/
- snew(pos_ins->nidx,1);
- snew(pos_ins->subindex,1);
- pos_ins->nidx[0]=ins_at->nr;
- pos_ins->subindex[0]=ins_at->index;
- }
-
- if(probe_rad<0.2199999)
- {
- warn++;
- fprintf(stderr,"\nWarning %d:\nA probe radius (-rad) smaller than 0.2 can result in overlap between waters "
- "and the group to embed, which will result in Lincs errors etc.\nIf you are sure, you can increase maxwarn.\n\n",warn);
- }
-
- if(xy_fac<0.09999999)
- {
- warn++;
- fprintf(stderr,"\nWarning %d:\nThe initial size of %s is probably too smal.\n"
- "If you are sure, you can increase maxwarn.\n\n",warn,ins);
- }
-
- if(it_xy<1000)
- {
- warn++;
- fprintf(stderr,"\nWarning %d;\nThe number of steps used to grow the xy-coordinates of %s (%d) is probably too small.\n"
- "Increase -nxy or, if you are sure, you can increase maxwarn.\n\n",warn,ins,it_xy);
- }
-
- if( (it_z<100) && ( z_fac<0.99999999 || z_fac>1.0000001) )
- {
- warn++;
- fprintf(stderr,"\nWarning %d;\nThe number of steps used to grow the z-coordinate of %s (%d) is probably too small.\n"
- "Increase -nz or, if you are sure, you can increase maxwarn.\n\n",warn,ins,it_z);
- }
-
- if(it_xy+it_z>inputrec->nsteps)
- {
- warn++;
- fprintf(stderr,"\nWarning %d:\nThe number of growth steps (-nxy + -nz) is larger than the number of steps in the tpr.\n"
- "If you are sure, you can increase maxwarn.\n\n",warn);
- }
-
- fr_id=-1;
- if( inputrec->opts.ngfrz==1)
- gmx_fatal(FARGS,"You did not specify \"%s\" as a freezegroup.",ins);
- for(i=0;i<inputrec->opts.ngfrz;i++)
- {
- tmp_id = mtop->groups.grps[egcFREEZE].nm_ind[i];
- if(ins_grp_id==tmp_id)
- {
- fr_id=tmp_id;
- fr_i=i;
- }
- }
- if (fr_id == -1 )
- gmx_fatal(FARGS,"\"%s\" not as freezegroup defined in the mdp-file.",ins);
-
- for(i=0;i<DIM;i++)
- if( inputrec->opts.nFreeze[fr_i][i] != 1)
- gmx_fatal(FARGS,"freeze dimensions for %s are not Y Y Y\n",ins);
-
- ng = groups->grps[egcENER].nr;
- if (ng == 1)
- gmx_input("No energy groups defined. This is necessary for energy exclusion in the freeze group");
-
- for(i=0;i<ng;i++)
- {
- for(j=0;j<ng;j++)
- {
- if (inputrec->opts.egp_flags[ng*i+j] == EGP_EXCL)
- {
- bExcl = TRUE;
- if ( (groups->grps[egcENER].nm_ind[i] != ins_grp_id) || (groups->grps[egcENER].nm_ind[j] != ins_grp_id) )
- gmx_fatal(FARGS,"Energy exclusions \"%s\" and \"%s\" do not match the group to embed \"%s\"",
- *groups->grpname[groups->grps[egcENER].nm_ind[i]],
- *groups->grpname[groups->grps[egcENER].nm_ind[j]],ins);
- }
- }
- }
- if (!bExcl)
- gmx_input("No energy exclusion groups defined. This is necessary for energy exclusion in the freeze group");
-
- /* Set all atoms in box*/
- /*set_inbox(state->natoms,state->x);*/
-
- /* Guess the area the protein will occupy in the membrane plane Calculate area per lipid*/
- snew(rest_at,1);
- ins_nat = init_ins_at(ins_at,rest_at,state,pos_ins,groups,ins_grp_id,xy_max);
- /* Check moleculetypes in insertion group */
- check_types(ins_at,rest_at,mtop);
-
- mem_nat = init_mem_at(mem_p,mtop,state->x,state->box,pos_ins);
-
- prot_area = est_prot_area(pos_ins,state->x,ins_at,mem_p);
- if ( (prot_area>7.5) && ( (state->box[XX][XX]*state->box[YY][YY]-state->box[XX][YY]*state->box[YY][XX])<50) )
- {
- warn++;
- fprintf(stderr,"\nWarning %d:\nThe xy-area is very small compared to the area of the protein.\n"
- "This might cause pressure problems during the growth phase. Just try with\n"
- "current setup (-maxwarn + 1), but if pressure problems occur, lower the\n"
- "compressibility in the mdp-file or use no pressure coupling at all.\n\n",warn);
- }
- if(warn>maxwarn)
- gmx_fatal(FARGS,"Too many warnings.\n");
-
- printf("The estimated area of the protein in the membrane is %.3f nm^2\n",prot_area);
- printf("\nThere are %d lipids in the membrane part that overlaps the protein.\nThe area per lipid is %.4f nm^2.\n",mem_p->nmol,mem_p->lip_area);
-
- /* Maximum number of lipids to be removed*/
- max_lip_rm=(int)(2*prot_area/mem_p->lip_area);
- printf("Maximum number of lipids that will be removed is %d.\n",max_lip_rm);
-
- printf("\nWill resize the protein by a factor of %.3f in the xy plane and %.3f in the z direction.\n"
- "This resizing will be done with respect to the geometrical center of all protein atoms\n"
- "that span the membrane region, i.e. z between %.3f and %.3f\n\n",xy_fac,z_fac,mem_p->zmin,mem_p->zmax);
-
- /* resize the protein by xy and by z if necessary*/
- snew(r_ins,ins_at->nr);
- init_resize(ins_at,r_ins,pos_ins,mem_p,state->x,bALLOW_ASYMMETRY);
- fac[0]=fac[1]=xy_fac;
- fac[2]=z_fac;
-
- xy_step =(xy_max-xy_fac)/(double)(it_xy);
- z_step =(z_max-z_fac)/(double)(it_z-1);
-
- resize(ins_at,r_ins,state->x,pos_ins,fac);
-
- /* remove overlapping lipids and water from the membrane box*/
- /*mark molecules to be removed*/
- snew(pbc,1);
- set_pbc(pbc,inputrec->ePBC,state->box);
-
- snew(rm_p,1);
- lip_rm = gen_rm_list(rm_p,ins_at,rest_at,pbc,mtop,state->x, r_ins, mem_p,pos_ins,probe_rad,low_up_rm,bALLOW_ASYMMETRY);
- lip_rm -= low_up_rm;
-
- if(fplog)
- for(i=0;i<rm_p->nr;i++)
- fprintf(fplog,"rm mol %d\n",rm_p->mol[i]);
-
- for(i=0;i<mtop->nmolblock;i++)
- {
- ntype=0;
- for(j=0;j<rm_p->nr;j++)
- if(rm_p->block[j]==i)
- ntype++;
- printf("Will remove %d %s molecules\n",ntype,*(mtop->moltype[mtop->molblock[i].type].name));
- }
-
- if(lip_rm>max_lip_rm)
- {
- warn++;
- fprintf(stderr,"\nWarning %d:\nTrying to remove a larger lipid area than the estimated protein area\n"
- "Try making the -xyinit resize factor smaller. If you are sure about this increase maxwarn.\n\n",warn);
- }
-
- /*remove all lipids and waters overlapping and update all important structures*/
- rm_group(inputrec,groups,mtop,rm_p,state,ins_at,pos_ins);
-
- rm_bonded_at = rm_bonded(ins_at,mtop);
- if (rm_bonded_at != ins_at->nr)
- {
- fprintf(stderr,"Warning: The number of atoms for which the bonded interactions are removed is %d, "
- "while %d atoms are embedded. Make sure that the atoms to be embedded are not in the same"
- "molecule type as atoms that are not to be embedded.\n",rm_bonded_at,ins_at->nr);
- }
-
- if(warn>maxwarn)
- gmx_fatal(FARGS,"Too many warnings.\nIf you are sure these warnings are harmless, you can increase -maxwarn");
-
- if (MASTER(cr))
- {
- if (ftp2bSet(efTOP,nfile,fnm))
- top_update(opt2fn("-p",nfile,fnm),ins,rm_p,mtop);
- }
-
- sfree(pbc);
- sfree(rest_at);
- }
-
-#ifdef GMX_FAHCORE
- fcRegisterSteps(inputrec->nsteps,inputrec->init_step);
-#endif
-
- /* NMR restraints must be initialized before load_checkpoint,
- * since with time averaging the history is added to t_state.
- * For proper consistency check we therefore need to extend
- * t_state here.
- * So the PME-only nodes (if present) will also initialize
- * the distance restraints.
- */
- snew(fcd,1);
-
- /* This needs to be called before read_checkpoint to extend the state */
- init_disres(fplog,mtop,inputrec,cr,Flags & MD_PARTDEC,fcd,state);
-
- if (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0)
- {
- if (PAR(cr) && !(Flags & MD_PARTDEC))
- {
- gmx_fatal(FARGS,"Orientation restraints do not work (yet) with domain decomposition, use particle decomposition (mdrun option -pd)");
- }
- /* Orientation restraints */
- if (MASTER(cr))
- {
- init_orires(fplog,mtop,state->x,inputrec,cr->ms,&(fcd->orires),
- state);
- }
- }
-
- if (DEFORM(*inputrec))
- {
- /* Store the deform reference box before reading the checkpoint */
- if (SIMMASTER(cr))
- {
- copy_mat(state->box,box);
- }
- if (PAR(cr))
- {
- gmx_bcast(sizeof(box),box,cr);
- }
- /* Because we do not have the update struct available yet
- * in which the reference values should be stored,
- * we store them temporarily in static variables.
- * This should be thread safe, since they are only written once
- * and with identical values.
- */
-/* deform_init_init_step_tpx = inputrec->init_step;*/
-/* copy_mat(box,deform_init_box_tpx);*/
- }
-
- if (opt2bSet("-cpi",nfile,fnm))
- {
- /* Check if checkpoint file exists before doing continuation.
- * This way we can use identical input options for the first and subsequent runs...
- */
- if( gmx_fexist_master(opt2fn_master("-cpi",nfile,fnm,cr),cr) )
- {
- load_checkpoint(opt2fn_master("-cpi",nfile,fnm,cr),&fplog,
- cr,Flags & MD_PARTDEC,ddxyz,
- inputrec,state,&bReadRNG,&bReadEkin,
- (Flags & MD_APPENDFILES));
-
- if (bReadRNG)
- {
- Flags |= MD_READ_RNG;
- }
- if (bReadEkin)
- {
- Flags |= MD_READ_EKIN;
- }
- }
- }
-
- if ((MASTER(cr) || (Flags & MD_SEPPOT)) && (Flags & MD_APPENDFILES))
- {
- gmx_log_open(ftp2fn(efLOG,nfile,fnm),cr,!(Flags & MD_SEPPOT),
- Flags,&fplog);
- }
-
- if (SIMMASTER(cr))
- {
- copy_mat(state->box,box);
- }
-
- if (PAR(cr))
- {
- gmx_bcast(sizeof(box),box,cr);
- }
-
- if (bVerbose && SIMMASTER(cr))
- {
- fprintf(stderr,"Loaded with Money\n\n");
- }
-
- if (PAR(cr) && !((Flags & MD_PARTDEC) || EI_TPI(inputrec->eI)))
- {
- cr->dd = init_domain_decomposition(fplog,cr,Flags,ddxyz,rdd,rconstr,
- dddlb_opt,dlb_scale,
- ddcsx,ddcsy,ddcsz,
- mtop,inputrec,
- box,state->x,
- &ddbox,&npme_major,&npme_minor);
-
- make_dd_communicators(fplog,cr,dd_node_order);
-
- /* Set overallocation to avoid frequent reallocation of arrays */
- set_over_alloc_dd(TRUE);
- }
- else
- {
- /* PME, if used, is done on all nodes with 1D decomposition */
- cr->npmenodes = 0;
- cr->duty = (DUTY_PP | DUTY_PME);
- npme_major = cr->nnodes;
- npme_minor = 1;
-
- if (inputrec->ePBC == epbcSCREW)
- {
- gmx_fatal(FARGS,
- "pbc=%s is only implemented with domain decomposition",
- epbc_names[inputrec->ePBC]);
- }
- }
-
- if (PAR(cr))
- {
- /* After possible communicator splitting in make_dd_communicators.
- * we can set up the intra/inter node communication.
- */
- gmx_setup_nodecomm(fplog,cr);
- }
-
- wcycle = wallcycle_init(fplog,resetstep,cr);
- if (PAR(cr))
- {
- /* Master synchronizes its value of reset_counters with all nodes
- * including PME only nodes */
- reset_counters = wcycle_get_reset_counters(wcycle);
- gmx_bcast_sim(sizeof(reset_counters),&reset_counters,cr);
- wcycle_set_reset_counters(wcycle, reset_counters);
- }
-
-
- snew(nrnb,1);
- if (cr->duty & DUTY_PP)
- {
- /* For domain decomposition we allocate dynamically
- * in dd_partition_system.
- */
- if (DOMAINDECOMP(cr))
- {
- bcast_state_setup(cr,state);
- }
- else
- {
- if (PAR(cr))
- {
- if (!MASTER(cr))
- {
- snew(state,1);
- }
- bcast_state(cr,state,TRUE);
- }
- }
-
- /* Dihedral Restraints */
- if (gmx_mtop_ftype_count(mtop,F_DIHRES) > 0)
- {
- init_dihres(fplog,mtop,inputrec,fcd);
- }
-
- /* Initiate forcerecord */
- fr = mk_forcerec();
- init_forcerec(fplog,oenv,fr,fcd,inputrec,mtop,cr,box,FALSE,
- opt2fn("-table",nfile,fnm),
- opt2fn("-tablep",nfile,fnm),
- opt2fn("-tableb",nfile,fnm),FALSE,pforce);
-
- /* version for PCA_NOT_READ_NODE (see md.c) */
- /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
- "nofile","nofile","nofile",FALSE,pforce);
- */
- fr->bSepDVDL = ((Flags & MD_SEPPOT) == MD_SEPPOT);
-
- /* Initialize QM-MM */
- if(fr->bQMMM)
- {
- init_QMMMrec(cr,box,mtop,inputrec,fr);
- }
-
- /* Initialize the mdatoms structure.
- * mdatoms is not filled with atom data,
- * as this can not be done now with domain decomposition.
- */
- mdatoms = init_mdatoms(fplog,mtop,inputrec->efep!=efepNO);
-
- /* Initialize the virtual site communication */
- vsite = init_vsite(mtop,cr);
-
- calc_shifts(box,fr->shift_vec);
-
- /* With periodic molecules the charge groups should be whole at start up
- * and the virtual sites should not be far from their proper positions.
- */
- if (!inputrec->bContinuation && MASTER(cr) &&
- !(inputrec->ePBC != epbcNONE && inputrec->bPeriodicMols))
- {
- /* Make molecules whole at start of run */
- if (fr->ePBC != epbcNONE)
- {
- do_pbc_first_mtop(fplog,inputrec->ePBC,box,mtop,state->x);
- }
- if (vsite)
- {
- /* Correct initial vsite positions are required
- * for the initial distribution in the domain decomposition
- * and for the initial shell prediction.
- */
- construct_vsites_mtop(fplog,vsite,mtop,state->x);
- }
- }
-
- /* Initiate PPPM if necessary */
- if (fr->eeltype == eelPPPM)
- {
- if (mdatoms->nChargePerturbed)
- {
- gmx_fatal(FARGS,"Free energy with %s is not implemented",
- eel_names[fr->eeltype]);
- }
- status = gmx_pppm_init(fplog,cr,oenv,FALSE,TRUE,box,
- getenv("GMXGHAT"),inputrec, (Flags & MD_REPRODUCIBLE));
- if (status != 0)
- {
- gmx_fatal(FARGS,"Error %d initializing PPPM",status);
- }
- }
-
- if (EEL_PME(fr->eeltype))
- {
- ewaldcoeff = fr->ewaldcoeff;
- pmedata = &fr->pmedata;
- }
- else
- {
- pmedata = NULL;
- }
- }
- else
- {
- /* This is a PME only node */
-
- /* We don't need the state */
- done_state(state);
-
- ewaldcoeff = calc_ewaldcoeff(inputrec->rcoulomb, inputrec->ewald_rtol);
- snew(pmedata,1);
- }
-
- /* Initiate PME if necessary,
- * either on all nodes or on dedicated PME nodes only. */
- if (EEL_PME(inputrec->coulombtype))
- {
- if (mdatoms)
- {
- nChargePerturbed = mdatoms->nChargePerturbed;
- }
- if (cr->npmenodes > 0)
- {
- /* The PME only nodes need to know nChargePerturbed */
- gmx_bcast_sim(sizeof(nChargePerturbed),&nChargePerturbed,cr);
- }
- if (cr->duty & DUTY_PME)
- {
- status = gmx_pme_init(pmedata,cr,npme_major,npme_minor,inputrec,
- mtop ? mtop->natoms : 0,nChargePerturbed,
- (Flags & MD_REPRODUCIBLE));
- if (status != 0)
- {
- gmx_fatal(FARGS,"Error %d initializing PME",status);
- }
- }
- }
-
-
-/* if (integrator[inputrec->eI].func == do_md
-#ifdef GMX_OPENMM
- ||
- integrator[inputrec->eI].func == do_md_openmm
-#endif
- )
- {*/
- /* Turn on signal handling on all nodes */
- /*
- * (A user signal from the PME nodes (if any)
- * is communicated to the PP nodes.
- */
- signal_handler_install();
-/* }*/
-
- if (cr->duty & DUTY_PP)
- {
- if (inputrec->ePull != epullNO)
- {
- /* Initialize pull code */
- init_pull(fplog,inputrec,nfile,fnm,mtop,cr,oenv,
- EI_DYNAMICS(inputrec->eI) && MASTER(cr),Flags);
- }
-
- constr = init_constraints(fplog,mtop,inputrec,ed,state,cr);
-
- if (DOMAINDECOMP(cr))
- {
- dd_init_bondeds(fplog,cr->dd,mtop,vsite,constr,inputrec,
- Flags & MD_DDBONDCHECK,fr->cginfo_mb);
-
- set_dd_parameters(fplog,cr->dd,dlb_scale,inputrec,fr,&ddbox);
-
- setup_dd_grid(fplog,cr->dd);
- }
-
- /* Now do whatever the user wants us to do (how flexible...) */
- do_md_membed(fplog,cr,nfile,fnm,
- oenv,bVerbose,bCompact,
- nstglobalcomm,
- vsite,constr,
- nstepout,inputrec,mtop,
- fcd,state,
- mdatoms,nrnb,wcycle,ed,fr,
- repl_ex_nst,repl_ex_seed,
- cpt_period,max_hours,
- deviceOptions,
- Flags,
- &runtime,
- fac, r_ins, pos_ins, ins_at,
- xy_step, z_step, it_xy, it_z);
-
- if (inputrec->ePull != epullNO)
- {
- finish_pull(fplog,inputrec->pull);
- }
- }
- else
- {
- /* do PME only */
- gmx_pmeonly(*pmedata,cr,nrnb,wcycle,ewaldcoeff,FALSE,inputrec);
- }
-
- if (EI_DYNAMICS(inputrec->eI) || EI_TPI(inputrec->eI))
- {
- /* Some timing stats */
- if (MASTER(cr))
- {
- if (runtime.proc == 0)
- {
- runtime.proc = runtime.real;
- }
- }
- else
- {
- runtime.real = 0;
- }
- }
-
- wallcycle_stop(wcycle,ewcRUN);
-
- /* Finish up, write some stuff
- * if rerunMD, don't write last frame again
- */
- finish_run(fplog,cr,ftp2fn(efSTO,nfile,fnm),
- inputrec,nrnb,wcycle,&runtime,
- EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
-
- /* Does what it says */
- print_date_and_time(fplog,cr->nodeid,"Finished mdrun",&runtime);
-
- /* Close logfile already here if we were appending to it */
- if (MASTER(cr) && (Flags & MD_APPENDFILES))
- {
- gmx_log_close(fplog);
- }
-
- if (pieces>1)
- {
- sfree(piecename);
- }
-
- rc=(int)gmx_get_stop_condition();
-
- return rc;
-}
-
int gmx_membed(int argc,char *argv[])
{
const char *desc[] = {
" - It is recommended to perform a short equilibration run after the embedding",
"(see Wolf et al, J Comp Chem 31 (2010) 2169-2174, to re-equilibrate the membrane. Clearly",
"protein equilibration might require longer.\n",
+ " - It is now also possible to use the g_membed functionality with mdrun. You should than pass",
+ "a data file containing the command line options of g_membed following the -membed option, for",
+ "example mdrun -s into_mem.tpr -membed membed.dat.",
"\n"
};
- t_commrec *cr;
t_filenm fnm[] = {
{ efTPX, "-f", "into_mem", ffREAD },
{ efNDX, "-n", "index", ffOPTRD },
{ efTOP, "-p", "topol", ffOPTRW },
{ efTRN, "-o", NULL, ffWRITE },
{ efXTC, "-x", NULL, ffOPTWR },
- { efCPT, "-cpi", NULL, ffOPTRD },
- { efCPT, "-cpo", NULL, ffOPTWR },
{ efSTO, "-c", "membedded", ffWRITE },
{ efEDR, "-e", "ener", ffWRITE },
- { efLOG, "-g", "md", ffWRITE },
- { efEDI, "-ei", "sam", ffOPTRD },
- { efTRX, "-rerun", "rerun", ffOPTRD },
- { efXVG, "-table", "table", ffOPTRD },
- { efXVG, "-tablep", "tablep", ffOPTRD },
- { efXVG, "-tableb", "table", ffOPTRD },
- { efXVG, "-dhdl", "dhdl", ffOPTWR },
- { efXVG, "-field", "field", ffOPTWR },
- { efXVG, "-table", "table", ffOPTRD },
- { efXVG, "-tablep", "tablep", ffOPTRD },
- { efXVG, "-tableb", "table", ffOPTRD },
- { efTRX, "-rerun", "rerun", ffOPTRD },
- { efXVG, "-tpi", "tpi", ffOPTWR },
- { efXVG, "-tpid", "tpidist", ffOPTWR },
- { efEDI, "-ei", "sam", ffOPTRD },
- { efEDO, "-eo", "sam", ffOPTWR },
- { efGCT, "-j", "wham", ffOPTRD },
- { efGCT, "-jo", "bam", ffOPTWR },
- { efXVG, "-ffout", "gct", ffOPTWR },
- { efXVG, "-devout", "deviatie", ffOPTWR },
- { efXVG, "-runav", "runaver", ffOPTWR },
- { efXVG, "-px", "pullx", ffOPTWR },
- { efXVG, "-pf", "pullf", ffOPTWR },
- { efMTX, "-mtx", "nm", ffOPTWR },
- { efNDX, "-dn", "dipole", ffOPTWR }
+ { efDAT, "-dat", "membed", ffWRITE }
};
#define NFILE asize(fnm)
/* Command line options ! */
- gmx_bool bCart = FALSE;
- gmx_bool bPPPME = FALSE;
- gmx_bool bPartDec = FALSE;
- gmx_bool bDDBondCheck = TRUE;
- gmx_bool bDDBondComm = TRUE;
- gmx_bool bVerbose = FALSE;
- gmx_bool bCompact = TRUE;
- gmx_bool bSepPot = FALSE;
- gmx_bool bRerunVSite = FALSE;
- gmx_bool bIonize = FALSE;
- gmx_bool bConfout = TRUE;
- gmx_bool bReproducible = FALSE;
-
- int npme=-1;
- int nmultisim=0;
- int nstglobalcomm=-1;
- int repl_ex_nst=0;
- int repl_ex_seed=-1;
- int nstepout=100;
- int nthreads=0; /* set to determine # of threads automatically */
- int resetstep=-1;
-
- rvec realddxyz={0,0,0};
- const char *ddno_opt[ddnoNR+1] =
- { NULL, "interleave", "pp_pme", "cartesian", NULL };
- const char *dddlb_opt[] =
- { NULL, "auto", "no", "yes", NULL };
- real rdd=0.0,rconstr=0.0,dlb_scale=0.8,pforce=-1;
- char *ddcsx=NULL,*ddcsy=NULL,*ddcsz=NULL;
- real cpt_period=15.0,max_hours=-1;
- gmx_bool bAppendFiles=TRUE,bAddPart=TRUE;
- gmx_bool bResetCountersHalfWay=FALSE;
- output_env_t oenv=NULL;
- const char *deviceOptions = "";
-
real xy_fac = 0.5;
real xy_max = 1.0;
real z_fac = 1.0;
int low_up_rm = 0;
int maxwarn=0;
int pieces=1;
- gmx_bool bALLOW_ASYMMETRY=FALSE;
-
+ gmx_bool bALLOW_ASYMMETRY=FALSE;
+ gmx_bool bStart=FALSE;
+ int nstepout=100;
+ gmx_bool bVerbose=FALSE;
+ char *mdrun_path=NULL;
/* arguments relevant to OPENMM only*/
#ifdef GMX_OPENMM
#endif
t_pargs pa[] = {
- { "-xyinit", FALSE, etREAL, {&xy_fac}, "Resize factor for the protein in the xy dimension before starting embedding" },
- { "-xyend", FALSE, etREAL, {&xy_max}, "Final resize factor in the xy dimension" },
- { "-zinit", FALSE, etREAL, {&z_fac}, "Resize factor for the protein in the z dimension before starting embedding" },
- { "-zend", FALSE, etREAL, {&z_max}, "Final resize faction in the z dimension" },
- { "-nxy", FALSE, etINT, {&it_xy}, "Number of iteration for the xy dimension" },
- { "-nz", FALSE, etINT, {&it_z}, "Number of iterations for the z dimension" },
- { "-rad", FALSE, etREAL, {&probe_rad}, "Probe radius to check for overlap between the group to embed and the membrane"},
- { "-pieces", FALSE, etINT, {&pieces}, "Perform piecewise resize. Select parts of the group to insert and resize these with respect to their own geometrical center." },
- { "-asymmetry",FALSE, etBOOL,{&bALLOW_ASYMMETRY}, "Allow asymmetric insertion, i.e. the number of lipids removed from the upper and lower leaflet will not be checked." },
- { "-ndiff" , FALSE, etINT, {&low_up_rm}, "Number of lipids that will additionally be removed from the lower (negative number) or upper (positive number) membrane leaflet." },
- { "-maxwarn", FALSE, etINT, {&maxwarn}, "Maximum number of warning allowed" },
- { "-pd", FALSE, etBOOL,{&bPartDec},
- "HIDDENUse particle decompostion" },
- { "-dd", FALSE, etRVEC,{&realddxyz},
- "HIDDENDomain decomposition grid, 0 is optimize" },
- { "-nt", FALSE, etINT, {&nthreads},
- "HIDDENNumber of threads to start (0 is guess)" },
- { "-npme", FALSE, etINT, {&npme},
- "HIDDENNumber of separate nodes to be used for PME, -1 is guess" },
- { "-ddorder", FALSE, etENUM, {ddno_opt},
- "HIDDENDD node order" },
- { "-ddcheck", FALSE, etBOOL, {&bDDBondCheck},
- "HIDDENCheck for all bonded interactions with DD" },
- { "-ddbondcomm", FALSE, etBOOL, {&bDDBondComm},
- "HIDDENUse special bonded atom communication when -rdd > cut-off" },
- { "-rdd", FALSE, etREAL, {&rdd},
- "HIDDENThe maximum distance for bonded interactions with DD (nm), 0 is determine from initial coordinates" },
- { "-rcon", FALSE, etREAL, {&rconstr},
- "HIDDENMaximum distance for P-LINCS (nm), 0 is estimate" },
- { "-dlb", FALSE, etENUM, {dddlb_opt},
- "HIDDENDynamic load balancing (with DD)" },
- { "-dds", FALSE, etREAL, {&dlb_scale},
- "HIDDENMinimum allowed dlb scaling of the DD cell size" },
- { "-ddcsx", FALSE, etSTR, {&ddcsx},
- "HIDDENThe DD cell sizes in x" },
- { "-ddcsy", FALSE, etSTR, {&ddcsy},
- "HIDDENThe DD cell sizes in y" },
- { "-ddcsz", FALSE, etSTR, {&ddcsz},
- "HIDDENThe DD cell sizes in z" },
- { "-gcom", FALSE, etINT,{&nstglobalcomm},
- "HIDDENGlobal communication frequency" },
- { "-compact", FALSE, etBOOL,{&bCompact},
- "Write a compact log file" },
- { "-seppot", FALSE, etBOOL, {&bSepPot},
- "HIDDENWrite separate V and dVdl terms for each interaction type and node to the log file(s)" },
- { "-pforce", FALSE, etREAL, {&pforce},
- "HIDDENPrint all forces larger than this (kJ/mol nm)" },
- { "-reprod", FALSE, etBOOL,{&bReproducible},
- "HIDDENTry to avoid optimizations that affect binary reproducibility" },
- { "-multi", FALSE, etINT,{&nmultisim},
- "HIDDENDo multiple simulations in parallel" },
- { "-replex", FALSE, etINT, {&repl_ex_nst},
- "HIDDENAttempt replica exchange every # steps" },
- { "-reseed", FALSE, etINT, {&repl_ex_seed},
- "HIDDENSeed for replica exchange, -1 is generate a seed" },
- { "-rerunvsite", FALSE, etBOOL, {&bRerunVSite},
- "HIDDENRecalculate virtual site coordinates with -rerun" },
- { "-ionize", FALSE, etBOOL,{&bIonize},
- "HIDDENDo a simulation including the effect of an X-Ray bombardment on your system" },
- { "-confout", TRUE, etBOOL, {&bConfout},
- "HIDDENWrite the last configuration with -c and force checkpointing at the last step" },
- { "-stepout", FALSE, etINT, {&nstepout},
- "HIDDENFrequency of writing the remaining runtime" },
- { "-resetstep", FALSE, etINT, {&resetstep},
- "HIDDENReset cycle counters after these many time steps" },
- { "-resethway", FALSE, etBOOL, {&bResetCountersHalfWay},
- "HIDDENReset the cycle counters after half the number of steps or halfway -maxh" },
- { "-v", FALSE, etBOOL,{&bVerbose},
- "Be loud and noisy" },
- { "-maxh", FALSE, etREAL, {&max_hours},
- "HIDDENTerminate after 0.99 times this time (hours)" },
- { "-cpt", FALSE, etREAL, {&cpt_period},
- "HIDDENCheckpoint interval (minutes)" },
- { "-append", FALSE, etBOOL, {&bAppendFiles},
- "HIDDENAppend to previous output files when continuing from checkpoint" },
- { "-addpart", FALSE, etBOOL, {&bAddPart},
- "HIDDENAdd the simulation part number to all output files when continuing from checkpoint" },
+ { "-xyinit", FALSE, etREAL, {&xy_fac},
+ "Resize factor for the protein in the xy dimension before starting embedding" },
+ { "-xyend", FALSE, etREAL, {&xy_max},
+ "Final resize factor in the xy dimension" },
+ { "-zinit", FALSE, etREAL, {&z_fac},
+ "Resize factor for the protein in the z dimension before starting embedding" },
+ { "-zend", FALSE, etREAL, {&z_max},
+ "Final resize faction in the z dimension" },
+ { "-nxy", FALSE, etINT, {&it_xy},
+ "Number of iteration for the xy dimension" },
+ { "-nz", FALSE, etINT, {&it_z},
+ "Number of iterations for the z dimension" },
+ { "-rad", FALSE, etREAL, {&probe_rad},
+ "Probe radius to check for overlap between the group to embed and the membrane"},
+ { "-pieces", FALSE, etINT, {&pieces},
+ "Perform piecewise resize. Select parts of the group to insert and resize these with respect to their own geometrical center." },
+ { "-asymmetry",FALSE, etBOOL,{&bALLOW_ASYMMETRY},
+ "Allow asymmetric insertion, i.e. the number of lipids removed from the upper and lower leaflet will not be checked." },
+ { "-ndiff" , FALSE, etINT, {&low_up_rm},
+ "Number of lipids that will additionally be removed from the lower (negative number) or upper (positive number) membrane leaflet." },
+ { "-maxwarn", FALSE, etINT, {&maxwarn},
+ "Maximum number of warning allowed" },
+ { "-start", FALSE, etBOOL, {&bStart},
+ "Call mdrun with membed options" },
+ { "-stepout", FALSE, etINT, {&nstepout},
+ "HIDDENFrequency of writing the remaining runtime" },
+ { "-v", FALSE, etBOOL,{&bVerbose},
+ "Be loud and noisy" },
+ { "-mdrun_path", FALSE, etSTR, {&mdrun_path},
+ "Path to the mdrun executable compiled with this g_membed version" }
};
- gmx_edsam_t ed;
- unsigned long Flags, PCA_Flags;
- ivec ddxyz;
- int dd_node_order;
- gmx_bool HaveCheckpoint;
- FILE *fplog,*fptest;
- int sim_part,sim_part_fn;
- const char *part_suffix=".part";
- char suffix[STRLEN];
- int rc;
-
-
- cr = init_par(&argc,&argv);
-
- PCA_Flags = (PCA_KEEP_ARGS | PCA_NOEXIT_ON_ARGS | PCA_CAN_SET_DEFFNM
- | (MASTER(cr) ? 0 : PCA_QUIET));
-
-
- /* Comment this in to do fexist calls only on master
- * works not with rerun or tables at the moment
- * also comment out the version of init_forcerec in md.c
- * with NULL instead of opt2fn
- */
- /*
- if (!MASTER(cr))
- {
- PCA_Flags |= PCA_NOT_READ_NODE;
- }
- */
-
- parse_common_args(&argc,argv,PCA_Flags, NFILE,fnm,asize(pa),pa,
- asize(desc),desc,0,NULL, &oenv);
-
- /* we set these early because they might be used in init_multisystem()
- Note that there is the potential for npme>nnodes until the number of
- threads is set later on, if there's thread parallelization. That shouldn't
- lead to problems. */
- dd_node_order = nenum(ddno_opt);
- cr->npmenodes = npme;
-
-#ifdef GMX_THREADS
- /* now determine the number of threads automatically. The threads are
- only started at mdrunner_threads, though. */
- if (nthreads<1)
- {
- nthreads=tMPI_Get_recommended_nthreads();
- }
-#else
- nthreads=1;
-#endif
+ FILE *data_out;
+ output_env_t oenv;
+ char buf[256],buf2[64];
- if (repl_ex_nst != 0 && nmultisim < 2)
- gmx_fatal(FARGS,"Need at least two replicas for replica exchange (option -multi)");
- if (nmultisim > 1) {
-#ifndef GMX_THREADS
- init_multisystem(cr,nmultisim,NFILE,fnm,TRUE);
-#else
- gmx_fatal(FARGS,"mdrun -multi is not supported with the thread library.Please compile GROMACS with MPI support");
-#endif
- }
-
- /* Check if there is ANY checkpoint file available */
- sim_part = 1;
- sim_part_fn = sim_part;
- if (opt2bSet("-cpi",NFILE,fnm))
- {
- bAppendFiles =
- read_checkpoint_simulation_part(opt2fn_master("-cpi", NFILE,fnm,cr),
- &sim_part_fn,NULL,cr,
- bAppendFiles,NFILE,fnm,
- part_suffix,&bAddPart);
- if (sim_part_fn==0 && MASTER(cr))
- {
- fprintf(stdout,"No previous checkpoint file present, assuming this is a new run.\n");
- }
- else
- {
- sim_part = sim_part_fn + 1;
- }
- }
- else
- {
- bAppendFiles = FALSE;
- }
+ parse_common_args(&argc,argv,0, NFILE,fnm,asize(pa),pa,
+ asize(desc),desc,0,NULL, &oenv);
- if (!bAppendFiles)
- {
- sim_part_fn = sim_part;
- }
+ data_out = ffopen(opt2fn("-dat",NFILE,fnm),"w");
+ fprintf(data_out,"nxy = %d\nnz = %d\nxyinit = %f\nxyend = %f\nzinit = %f\nzend = %f\n"
+ "rad = %f\npieces = %d\nasymmetry = %s\nndiff = %d\nmaxwarn = %d\n",
+ it_xy,it_z,xy_fac,xy_max,z_fac,z_max,probe_rad,pieces,
+ bALLOW_ASYMMETRY ? "yes" : "no",low_up_rm,maxwarn);
+ fclose(data_out);
- if (bAddPart && sim_part_fn > 1)
+ sprintf(buf,"%s -s %s -membed %s -o %s -c %s -e %s -nt 1 -cpt -1",
+ (mdrun_path==NULL) ? "mdrun" : mdrun_path,
+ opt2fn("-f",NFILE,fnm),opt2fn("-dat",NFILE,fnm),opt2fn("-o",NFILE,fnm),
+ opt2fn("-c",NFILE,fnm),opt2fn("-e",NFILE,fnm));
+ if (opt2bSet("-n",NFILE,fnm))
{
- /* This is a continuation run, rename trajectory output files
- (except checkpoint files) */
- /* create new part name first (zero-filled) */
- sprintf(suffix,"%s%04d",part_suffix,sim_part_fn);
-
- add_suffix_to_output_names(fnm,NFILE,suffix);
- fprintf(stdout,"Checkpoint file is from part %d, new output files will be suffixed '%s'.\n",sim_part-1,suffix);
- }
-
- Flags = opt2bSet("-rerun",NFILE,fnm) ? MD_RERUN : 0;
- Flags = Flags | (bSepPot ? MD_SEPPOT : 0);
- Flags = Flags | (bIonize ? MD_IONIZE : 0);
- Flags = Flags | (bPartDec ? MD_PARTDEC : 0);
- Flags = Flags | (bDDBondCheck ? MD_DDBONDCHECK : 0);
- Flags = Flags | (bDDBondComm ? MD_DDBONDCOMM : 0);
- Flags = Flags | (bConfout ? MD_CONFOUT : 0);
- Flags = Flags | (bRerunVSite ? MD_RERUN_VSITE : 0);
- Flags = Flags | (bReproducible ? MD_REPRODUCIBLE : 0);
- Flags = Flags | (bAppendFiles ? MD_APPENDFILES : 0);
- Flags = Flags | (sim_part>1 ? MD_STARTFROMCPT : 0);
- Flags = Flags | (bResetCountersHalfWay ? MD_RESETCOUNTERSHALFWAY : 0);
-
-
- /* We postpone opening the log file if we are appending, so we can
- first truncate the old log file and append to the correct position
- there instead. */
- if ((MASTER(cr) || bSepPot) && !bAppendFiles)
+ sprintf(buf2," -mn %s",opt2fn("-n",NFILE,fnm));
+ strcat(buf,buf2);
+ }
+ if (opt2bSet("-x",NFILE,fnm))
{
- gmx_log_open(ftp2fn(efLOG,NFILE,fnm),cr,!bSepPot,Flags,&fplog);
- CopyRight(fplog,argv[0]);
- please_cite(fplog,"Hess2008b");
- please_cite(fplog,"Spoel2005a");
- please_cite(fplog,"Lindahl2001a");
- please_cite(fplog,"Berendsen95a");
+ sprintf(buf2," -x %s",opt2fn("-x",NFILE,fnm));
+ strcat(buf,buf2);
}
- else
+ if (opt2bSet("-p",NFILE,fnm))
+ {
+ sprintf(buf2," -mp %s",opt2fn("-p",NFILE,fnm));
+ strcat(buf,buf2);
+ }
+ if (bVerbose)
{
- fplog = NULL;
- }
-
- ddxyz[XX] = (int)(realddxyz[XX] + 0.5);
- ddxyz[YY] = (int)(realddxyz[YY] + 0.5);
- ddxyz[ZZ] = (int)(realddxyz[ZZ] + 0.5);
-
- /* even if nthreads = 1, we still call this one */
-
- rc = mdrunner_membed(fplog, cr, NFILE, fnm, oenv, bVerbose, bCompact,
- nstglobalcomm,
- ddxyz, dd_node_order, rdd, rconstr, dddlb_opt[0], dlb_scale,
- ddcsx, ddcsy, ddcsz, nstepout, resetstep, nmultisim, repl_ex_nst,
- repl_ex_seed, pforce, cpt_period, max_hours, deviceOptions, Flags,
- xy_fac,xy_max,z_fac,z_max,
- it_xy,it_z,probe_rad,low_up_rm,
- pieces,bALLOW_ASYMMETRY,maxwarn);
-
- if (gmx_parallel_env_initialized())
- gmx_finalize();
-
- if (MULTIMASTER(cr)) {
- thanx(stderr);
+ sprintf(buf2," -v -stepout %d",nstepout);
+ strcat(buf,buf2);
}
- /* Log file has to be closed in mdrunner if we are appending to it
- (fplog not set here) */
- fprintf(stderr,"Please cite:\nWolf et al, J Comp Chem 31 (2010) 2169-2174.\n");
+ printf("%s\n",buf);
+ if (bStart)
+ {
+ system(buf);
+ } else {
+ printf("You can membed your protein now by:\n%s\n",buf);
+ }
- if (MASTER(cr) && !bAppendFiles)
- {
- gmx_log_close(fplog);
- }
+ fprintf(stderr,"Please cite:\nWolf et al, J Comp Chem 31 (2010) 2169-2174.\n");
- return rc;
+ return 0;
}
for(j=0; (j<curr->nrestart); j++) {
real xx,yy,dx,dy;
- while(gmx_stats_get_point(curr->lsq[j][i],&xx,&yy,&dx,&dy) == estatsOK)
+ while(gmx_stats_get_point(curr->lsq[j][i],&xx,&yy,&dx,&dy,0) == estatsOK)
gmx_stats_add_point(lsq1,xx,yy,dx,dy);
}
gmx_stats_get_ab(lsq1,elsqWEIGHT_NONE,&a,&b,NULL,NULL,NULL,NULL);
cr = init_par(&argc,&argv);
#ifdef GMX_MPI
- MPI_Barrier(MPI_COMM_WORLD);
+ if (PAR(cr))
+ MPI_Barrier(MPI_COMM_WORLD);
#endif
if (MASTER(cr))
const char *desc[] = {
"g_saltbr plots the distance between all combination of charged groups",
"as a function of time. The groups are combined in different ways.",
- "A minimum distance can be given, (eg. the cut-off), then groups",
- "that are never closer than that distance will not be plotted.[BR]",
+ "A truncation/cut-off distance can be supplied with -t, ",
+ "such that only groups interacting within this distance will be plotted in the ",
+ "output.[BR]",
"Output will be in a number of fixed filenames, min-min.xvg, plus-min.xvg",
"and plus-plus.xvg, or files for every individual ion-pair if the [TT]-sep[tt]",
"option is selected. In this case files are named as [TT]sb-ResnameResnr-Atomnr[tt].",
#include <statutil.h>
#include <xvgr.h>
#include <string2.h>
-#include <trajana.h>
#include "gmx_ana.h"
#include "gmx_fatal.h"
+#include "gromacs/trajana/trajana.h"
typedef struct
{
* not on mdrun command line options! */
static gmx_bool tpr_triggers_file(const char *opt)
{
- if ( (0 == strcmp(opt, "-pf"))
+ if ( (0 == strcmp(opt, "-ro"))
+ || (0 == strcmp(opt, "-ra"))
+ || (0 == strcmp(opt, "-rt"))
+ || (0 == strcmp(opt, "-rs"))
+ || (0 == strcmp(opt, "-pf"))
|| (0 == strcmp(opt, "-px")) )
return TRUE;
else
{ efXVG, "-runav", "runaver", ffOPTWR },
{ efXVG, "-px", "pullx", ffOPTWR },
{ efXVG, "-pf", "pullf", ffOPTWR },
+ { efXVG, "-ro", "rotation", ffOPTWR },
+ { efLOG, "-ra", "rotangles",ffOPTWR },
+ { efLOG, "-rs", "rotslabs", ffOPTWR },
+ { efLOG, "-rt", "rottorque",ffOPTWR },
{ efMTX, "-mtx", "nm", ffOPTWR },
{ efNDX, "-dn", "dipole", ffOPTWR },
/* Output files that are deleted after each benchmark run */
{ efXVG, "-brunav", "benchrnav",ffOPTWR },
{ efXVG, "-bpx", "benchpx", ffOPTWR },
{ efXVG, "-bpf", "benchpf", ffOPTWR },
+ { efXVG, "-bro", "benchrot", ffOPTWR },
+ { efLOG, "-bra", "benchrota",ffOPTWR },
+ { efLOG, "-brs", "benchrots",ffOPTWR },
+ { efLOG, "-brt", "benchrott",ffOPTWR },
{ efMTX, "-bmtx", "benchn", ffOPTWR },
{ efNDX, "-bdn", "bench", ffOPTWR }
};